Configuration
Rustybin is configured through environment variables; the only command-line
option of the server is --config <file>, the rustybin.yaml declaring
OpenAPI mocks. Invalid values never
stop the server: they are logged as warnings and the default is used. Values are
trimmed, and an empty value counts as unset (except for RUSTYBIN_CORS_ORIGINS, see
below). Booleans accept 1, true, yes, on and 0, false, no, off
(case-insensitive).
GET /_rustybin/config returns the effective configuration without secrets (the
admin token is only reported as admin_token_configured).
This list is checked against src/config.rs and every std::env::var call in
src/ (the MCP, mock LLM and A2A modules read a few variables of their own).
Listeners and process
| Variable | Default | Description |
|---|---|---|
RUSTYBIN_HTTP_PORT | 80 | HTTP port. Failing to bind it is fatal (exit code 1). 0 picks a free port. |
RUSTYBIN_HTTPS_PORT | 443 | HTTPS port (same routes as HTTP). Optional: a bind or certificate failure only logs a warning. |
RUSTYBIN_GRPC_PORT | 50051 | gRPC port (EchoService, health, reflection, gRPC-Web). Optional, like HTTPS. |
RUSTYBIN_HOST | 0.0.0.0 | Bind address for all listeners: an IPv4 or IPv6 address (::, [::]) or localhost (127.0.0.1). |
RUSTYBIN_LOG_LEVEL | info | Log filter in tracing EnvFilter syntax (debug, warn, rustybin=debug,tower_http=info, ...). Falls back to RUST_LOG; an unparsable filter means info. Logs go to stdout, coloured only when stdout is a terminal. |
RUSTYBIN_LOG_FORMAT | text | text or json (one JSON object per line, with the request's method, path and request_id in a span object). json by default in hosted mode. See Logs. |
RUSTYBIN_GRPC_ON_HTTP | false | Also serve the gRPC services on the HTTP and HTTPS listeners for requests with content-type: application/grpc* (HTTP/2; the HTTPS listener then offers h2 through ALPN), so a TLS-terminating proxy can forward gRPC on 443. See gRPC. |
RUSTYBIN_INSTANCE_ID | random UUID | Instance name returned by /identity, /health, the mock LLM (X-Rustybin-Instance) and the gRPC EchoService. Set it per replica for load balancing demos. |
RUSTYBIN_REQUEST_TIMEOUT | 120 | Seconds until the response headers must be ready, else 503. Streaming bodies (SSE, WebSocket, slow drips) are not cut once headers are sent. 0 disables the timeout. |
RUSTYBIN_BODY_LIMIT | 1048576 | Maximum request body in bytes; larger bodies get 413. Also the size up to which /echo shows a body. |
The process stops gracefully on SIGTERM or SIGINT: listeners stop accepting and
in-flight connections (streams included) get 10 seconds to finish. GET /_rustybin/ready
is the readiness probe (always 200 while serving); GET / the liveness check.
The Docker image runs as an unprivileged user and supports a read-only root
filesystem, see Containers.
Proxies and TLS
| Variable | Default | Description |
|---|---|---|
RUSTYBIN_TRUST_FORWARD | false | Trust proxy headers. Client IP: Fly-Client-IP, then RFC 7239 Forwarded: for=, X-Forwarded-For (the rightmost address that is not private or loopback, so clients cannot spoof it by prepending), then X-Real-IP. Scheme, host and port: X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port and Forwarded: proto= host=. The OIDC issuer also honours X-Forwarded-Prefix. Enable it only behind a proxy you control. |
RUSTYBIN_TLS_CERT | certs/server.crt | Certificate of the HTTPS listener (relative to the working directory). |
RUSTYBIN_TLS_KEY | certs/server.key | Private key of the HTTPS listener. |
RUSTYBIN_MTLS_IN_HEADER | unset | Name of a request header in which a gateway that terminated mTLS forwards the client certificate (URL-encoded PEM, raw PEM or base64 DER). /auth/mtls then verifies it against the demo CA. See Authentication. |
Demo PKI. At startup Rustybin loads a demo CA from ca.crt and ca.key in the
directory of RUSTYBIN_TLS_CERT, or generates one and writes it there (best effort),
so client certificates issued before a restart stay valid. Rustybin only reuses or
replaces files it created (a CA certificate with the subject CN=Rustybin Demo CA): if
ca.crt / ca.key hold another CA, such as your own, they are left untouched with a
warning and the demo CA lives in rustybin-demo-ca.crt / rustybin-demo-ca.key instead
(if those names are taken by something else too, it is kept in memory only). For the
HTTPS listener:
- if both
RUSTYBIN_TLS_CERTandRUSTYBIN_TLS_KEYexist, they are used (a stale pair issued by an older demo CA is replaced; a key that does not match the certificate is an error and HTTPS does not start); - if neither exists, a server certificate for
localhost,127.0.0.1,::1andrustybinsigned by the demo CA is generated and written there; - if only one exists, nothing is written and the generated pair is used in memory.
Download the CA and a client certificate from /auth/mtls/get-ca-cert and
/auth/mtls/get-client-cert. The HTTPS listener asks for a client certificate but
does not require one; when one is presented it must chain to the demo CA.
Shared instances
| Variable | Default | Description |
|---|---|---|
RUSTYBIN_PUBLIC_MODE | false | Hardening for shared, internet-facing instances: lower caps everywhere, the inspector only captures requests tagged with X-Rustybin-Session, and instance-global mutations are refused unless an admin token is configured. See Sessions, public mode and the admin token. |
RUSTYBIN_ADMIN_TOKEN | unset | When set, instance-global mutations (health toggles, POST /flaky/reset?scope=all, clearing every captured request) need Authorization: Bearer <token> or X-Rustybin-Admin-Token: <token> (or a control-plane JWT with the admin scope). With RUSTYBIN_CONTROL_AUTH=token or jwt it also opens the whole control plane. Never returned by any endpoint. |
RUSTYBIN_CORS_ORIGINS | * | Allowed CORS origins, comma separated. off or an empty value removes the CORS layer entirely, so a gateway's own CORS plugin can be demonstrated. |
RUSTYBIN_INSPECTOR_CAPACITY | 500 | Requests kept by the request inspector ring buffer (1 to 10000). |
Control plane and console
See Control-plane security, metrics and operations.
| Variable | Default | Description |
|---|---|---|
RUSTYBIN_CONTROL_AUTH | open | open: /_rustybin/* needs no credentials. token: every /_rustybin/* route except /_rustybin/ready needs RUSTYBIN_ADMIN_TOKEN. jwt: the admin token or an Ed25519 JWT signed by RUSTYBIN_CONTROL_JWT_PUBLIC_KEY (scopes inspector, console, admin). Data-plane routes and the console's static files are never affected. jwt by default in hosted mode. |
RUSTYBIN_CONTROL_JWT_PUBLIC_KEY | unset | Ed25519 public key for control-plane JWTs: PEM (newlines may be \n), base64 DER, or base64 / base64url of the raw 32 bytes. Invalid: warning, JWTs refused. |
RUSTYBIN_CONTROL_JWT_AUDIENCE | RUSTYBIN_INSTANCE_ID | Required aud claim of control-plane JWTs. |
RUSTYBIN_HOSTED_MODE | false | Preset for platforms running one instance per customer: control auth jwt and JSON logs, unless RUSTYBIN_CONTROL_AUTH / RUSTYBIN_LOG_FORMAT say otherwise. |
RUSTYBIN_CONSOLE_TITLE | unset | Shown in the console header and tab title (control characters removed, at most 80 characters). |
RUSTYBIN_CONSOLE_BACKLINK | unset | http:// or https:// URL shown as a "Back to ..." link in the console header and sign-in screen; anything else is ignored with a warning. |
OpenAPI mocks
| Variable | Default | Description |
|---|---|---|
RUSTYBIN_CONFIG_FILE | ./rustybin.yaml if it exists | A rustybin.yaml declaring OpenAPI mocks, loaded at startup and reloaded by SIGHUP or POST /_rustybin/mocks/reload. The command-line option --config <file> takes precedence. Problems are logged and listed in GET /_rustybin/mocks (load_errors); they never stop the server. |
RUSTYBIN_MOCKS_DIR | unset | A directory whose *.yaml, *.yml and *.json OpenAPI files (up to 200) each become a mock named after the file, with default settings. |
RUSTYBIN_MOCKS_SAMPLE | true (false in public mode) | Create the built-in petstore mock. |
RUSTYBIN_MOCKS_STATE_DIR | unset | Save mocks created or edited through the API or console in this directory (one JSON file each, written on every change) and load them at startup. File-defined mocks are not saved. See Configuration as code. |
RUSTYBIN_MOCKS_HOST | unset | Domain for host-based mock URLs, e.g. mock.example.com: a request to petstore.mock.example.com/v1/pets is served by the mock petstore. Needs wildcard DNS (and a wildcard certificate for HTTPS). See Configuration as code. |
${VAR} and ${VAR:-default} in the config file are expanded from the environment
(see Environment variables). The
rustybin mock CLI reads RUSTYBIN_SERVER (default
http://localhost on RUSTYBIN_HTTP_PORT, port 80 when unset) and RUSTYBIN_TOKEN (else RUSTYBIN_ADMIN_TOKEN) as
defaults for --server and --token.
Plan limits
Off by default. See Plans and limits for the presets, the response headers and what is exempt.
| Variable | Default | Description |
|---|---|---|
RUSTYBIN_PLAN | none | none (nothing enforced, no extra header), free, pro, team or enterprise (no limits, plan name reported). |
RUSTYBIN_LIMIT_RPS | plan | Sustained requests per second (token bucket refill rate). |
RUSTYBIN_LIMIT_BURST | plan | Token bucket capacity; 0 means the same as the rps value. |
RUSTYBIN_LIMIT_CONCURRENCY | plan | Requests in flight at once (a response counts until its body is sent). |
RUSTYBIN_LIMIT_STREAMS | plan | Open SSE responses and WebSocket connections at once. |
RUSTYBIN_LIMIT_STREAM_SECS | plan | Maximum stream lifetime in seconds; longer streams are ended cleanly. |
RUSTYBIN_LIMIT_REQUESTS | plan | Requests per period. |
RUSTYBIN_LIMIT_EGRESS_MB | plan | Response body megabytes per period (1 MB = 1,000,000 bytes, fractions such as 0.5 allowed). |
RUSTYBIN_LIMIT_PERIOD | plan | Quota period: day or month (calendar UTC). |
RUSTYBIN_LIMIT_SCOPE | plan | instance (one set of counters) or session (one set per X-Rustybin-Session, else client IP). |
RUSTYBIN_USAGE_FILE | unset | Path of a JSON file holding the instance quota counters, written every 30 s and on graceful shutdown and read at startup, so a restart cannot reset a monthly quota. Session counters are never persisted. |
Every RUSTYBIN_LIMIT_* value accepts 0 or unlimited to turn that dimension off.
Setting any of them without a plan enables the limiter with the plan name custom.
API keys and shared mode
Off by default. See Shared mode and API keys for the registry format, quotas, scoping and the control plane.
| Variable | Default | Description |
|---|---|---|
RUSTYBIN_SHARED_MODE | false | One instance for many API keys: every data-plane request needs a key (X-Rustybin-Key, else ?rb_key=), each key has its own limits and daily quotas, inspector and usage are scoped by key, the rest of the control plane needs the admin token. Implies RUSTYBIN_PUBLIC_MODE, RUSTYBIN_LIMIT_SCOPE=session and RUSTYBIN_LIMIT_PERIOD=day. |
RUSTYBIN_REQUIRE_KEY | false | On a normal instance: the data plane needs a valid key from the registry; no per-key limits, control-plane auth unchanged. |
RUSTYBIN_KEYS_FILE | unset | Key registry file (JSON), read at startup and reloaded when it changes (checked every 5 s). Wins over RUSTYBIN_KEYS_URL. |
RUSTYBIN_KEYS_URL | unset | Key registry URL (http:// or https://), fetched at startup and every RUSTYBIN_KEYS_POLL_SECS with If-None-Match. Errors keep the last good registry. |
RUSTYBIN_KEYS_TOKEN | unset | Sent to RUSTYBIN_KEYS_URL as Authorization: Bearer <token>. Never returned by any endpoint. |
RUSTYBIN_KEYS_POLL_SECS | 30 | Registry URL poll interval in seconds (every 5 s at most while nothing was loaded yet). |
RUSTYBIN_KEYS_MAX | 200000 | Most keys accepted in a registry; a larger registry is refused with a warning (the last good one stays). |
RUSTYBIN_QUOTA_STORE | unset | postgres://... URL of a PostgreSQL database shared by the replicas for daily quotas (table key_usage, created when missing). Needs a binary built with --features quota-postgres; otherwise a warning and local counting. Never returned by any endpoint. |
RUSTYBIN_QUOTA_STORE_CA | unset | PEM file with extra CA certificates for the quota store's TLS (besides the public web roots). |
RUSTYBIN_QUOTA_FLUSH_MS | 2000 | How often each replica flushes its counts to the quota store, in milliseconds. |
RUSTYBIN_QUOTA_UPGRADE_URL | unset | http:// or https:// link returned as upgrade_url in daily quota 429 bodies (null when unset). |
MCP server
Read when the server starts.
| Variable | Default | Description |
|---|---|---|
RUSTYBIN_MCP_API_KEY | unset | The only X-API-Key value /mcp/apikey accepts. Unset: any non-empty key is accepted. |
RUSTYBIN_MCP_ALLOWED_ORIGINS | * | Comma-separated Origin values accepted by every MCP endpoint (DNS rebinding protection). Requests with another Origin get 403; requests without Origin are always accepted. |
RUSTYBIN_MCP_ACCEPTED_AUDIENCES | rustybin | Token audiences /mcp/protected accepts besides its own resource URL. The default is the identity provider's default audience, so a plain client_credentials token works. none accepts only tokens issued for the resource (strict RFC 8707). |
RUSTYBIN_MCP_RESOURCE_URL | derived | Override the resource identifier of /mcp/protected (by default <scheme>://<host>/mcp/protected from the request). Set it to the gateway's public URL when the gateway does not forward Host. |
RUSTYBIN_MCP_CLOCK_TICK_SECS | 5 | Update interval of the subscribable rustybin://clock resource (1 to 3600). |
Mock LLM
| Variable | Default | Description |
|---|---|---|
RUSTYBIN_AI_REQUIRE_AUTH | false | Require each provider's native credential on every /ai/* call (per request: X-Rustybin-Require-Auth: true). |
RUSTYBIN_AI_API_KEY | unset | The only accepted key (implies RUSTYBIN_AI_REQUIRE_AUTH). For Bedrock SigV4 it is compared with the access key id. |
See Faults, latency, credentials, inspection.
A2A agents
| Variable | Default | Description |
|---|---|---|
RUSTYBIN_A2A_PUSH_ALLOWLIST | unset | Comma-separated host or host:port entries that push notification URLs may point to. The built-in sink /a2a/webhook-sink/{id} on this server is always allowed. |
RUSTYBIN_A2A_PUSH_ALLOW_ALL | false | Allow push notifications to any http(s) URL, including loopback and private addresses. Ignored in public mode. |
Development only
| Variable | Used by |
|---|---|
RUSTYBIN_UPDATE_README | RUSTYBIN_UPDATE_README=1 cargo test readme_endpoints rewrites the endpoint tables in README.md. |
RUSTYBIN_BIN, RUSTYBIN_DOCS_PORT, REQUIRE_ALL_TOOLS | docs/examples/run.sh (see How the examples work). |
PYTHON, RUSTYBIN_CONFORMANCE_PORT, RUSTYBIN_URL, MCP_BASE | The SDK conformance scripts under conformance/. |
RUSTYBIN_UI_PORT, RUSTYBIN_JWT_URL, CONTROL_JWT_PRIVATE_KEY, CONTROL_JWT_AUDIENCE | conformance/ui/run.sh and console_e2e.py (the console check, including the sign-in flow). |
RUSTYBIN_GIT_SHA | Build time: the commit reported by /_rustybin/version and the metrics when the source tree has no .git (Docker builds: --build-arg RUSTYBIN_GIT_SHA=<commit>). |
Limits that are not configurable
Request-reachable state is always bounded. The main caps (normal mode / public mode):
| What | Normal | Public |
|---|---|---|
X-Rustybin-Delay, /drip duration and delay, JSON-RPC sleep, bin response delay | 30 s | 10 s |
/delay/{duration} | 60 s | 10 s |
/bytes, /stream-bytes, /range size | 100 KiB | 10 KiB |
/stream/{n} lines | 100 | 20 |
/drip bytes | 10240 | 1024 |
/sse events / interval / stream lifetime | 1000 / 60 s / 10 min | 100 / 10 s / 2 min |
/sse/chat tokens / delay per token | 500 / 2 s | 200 / 1 s |
| JSON-RPC batch size | 100 | 20 |
| WebSocket message size / idle timeout / lifetime | 1 MiB / 5 min / 1 h | 256 KiB / 1 min / 10 min |
| Request bins (total / per session / requests per bin / bytes per bin / TTL) | 200 / 200 / 100 / 1 MiB / 24 h | 100 / 10 / 50 / 256 KiB / 1 h |
| Flaky counters (idle TTL 1 h) | 10000 | 2000 |
| OAuth codes, refresh tokens, revocations / registered clients | 10000 / 1000 | 2000 / 200 |
Mock LLM request records (1 h TTL) / choices n / embedding dimensions | 1000 / 8 / 4096 | 200 / 2 / 1536 |
| MCP sessions (idle TTL) / long-lived streams | 1000 (30 min) / 1 h | 200 (10 min) / 5 min |
| A2A tasks (total / per session / idle TTL) | 5000 / 1000 / 1 h | 2000 / 50 / 15 min |
| OpenAPI mocks (mocks / spec size / operations / captured requests per mock / lifetime of API-created mocks) | 100 / 1 MiB / 500 / 200 / unlimited | 50 / 256 KiB / 200 / 50 / 24 h |
| Inspector entries | RUSTYBIN_INSPECTOR_CAPACITY (max 10000) | same |
Metrics series (route values / LLM token series) | 1024 / 256 (beyond: other) | same |