Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quick start

All examples on this page assume Rustybin answers on http://localhost:8080.

Docker

There is no published image: build it from the repository (the build compiles a release binary, which takes a few minutes the first time).

git clone https://github.com/oliwynn/rustybin && cd rustybin
docker build -t rustybin .
docker run -d --name rustybin -p 8080:80 -p 8443:443 -p 50051:50051 rustybin

The container listens on 80 (HTTP), 443 (HTTPS) and 50051 (gRPC) and runs as an unprivileged user (uid 10001). On first start it generates a demo PKI in /app/certs (a CA, the HTTPS server certificate and a client certificate for mTLS demos); mount a volume on /app/certs to keep it across restarts (a bind-mounted host directory must be writable by uid 10001). The image also runs with --read-only when /app/certs is a volume, see Containers. Settings are environment variables, see Configuration:

docker run -d -p 8080:80 -e RUSTYBIN_INSTANCE_ID=demo-1 -e RUSTYBIN_ADMIN_TOKEN=change-me rustybin

Docker Compose

docker-compose.yml builds the image, publishes ports 80, 443 and 50051 and keeps the demo PKI in the named volume rustybin-certs so it survives restarts (download the CA with curl -o ca.crt http://localhost/auth/mtls/get-ca-cert):

docker compose up -d
curl http://localhost/health

The file also contains a commented-out block for two more instances (rustybin-02, rustybin-03) with their own RUSTYBIN_INSTANCE_ID, for load balancing demos (see Identity and load balancing).

Cargo

Rust 1.88 or newer. The default ports (80 and 443) need root, so pick high ports:

RUSTYBIN_HTTP_PORT=8080 RUSTYBIN_HTTPS_PORT=8443 cargo run --release

cargo run writes the demo PKI to certs/ in the working directory (the default RUSTYBIN_TLS_CERT is certs/server.crt); the files are git-ignored. Other flags:

cargo run -- --version                    # print the version
cargo run -- --print-endpoints-markdown   # print the endpoint tables (from the route catalogue)
cargo run -- --config rustybin.yaml       # run with OpenAPI mocks declared in a file
cargo run -- mock --help                  # the OpenAPI mock CLI

See OpenAPI mocks for rustybin.yaml, mounting specs into the container and the rustybin mock commands.

Fly.io

fly.toml deploys the Dockerfile with the web surface behind Fly's edge TLS and the gRPC port as raw TCP passthrough:

fly launch --copy-config --no-deploy   # once: pick an app name and region
fly deploy

Things to know about the provided configuration:

  • RUSTYBIN_TRUST_FORWARD=true: Fly's proxy sets Fly-Client-IP and X-Forwarded-*, so /ip, /echo and the OIDC issuer report the real client and https scheme.
  • The HTTP health check targets GET /_rustybin/ready (always 200 while serving), not /health, which is a demo toggle that can return 503.
  • The image runs as an unprivileged user, so the configuration moves the listeners to ports 8080 (HTTP, Fly's internal_port) and 8443 (HTTPS).
  • To serve gRPC through Fly's TLS edge on 443 instead of a raw port, set RUSTYBIN_GRPC_ON_HTTP=true and give the 443 service an HTTP/2 backend (see gRPC).
  • Because Fly terminates TLS for the web surface, Rustybin's own HTTPS listener and mTLS on it are not reachable; use the RUSTYBIN_MTLS_IN_HEADER mode for mTLS demos behind a gateway instead (see Authentication).
  • gRPC on port 50051 is plaintext h2c: grpcurl -plaintext <app>.fly.dev:50051 list.
  • For a shared, internet-facing instance set RUSTYBIN_PUBLIC_MODE=true (see Sessions, public mode and the admin token).

First requests

# Echo: see exactly what arrived (method, path, query, headers, body)
curl -s 'http://localhost:8080/anything/hello?demo=1' -H 'X-Demo: rustybin'

# Mock LLM, OpenAI wire format
curl -s http://localhost:8080/ai/openai/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"gpt-4o","messages":[{"role":"user","content":"hello"}]}'

# An OpenAPI mock: the built-in Petstore sample (upload your own spec in /ui/#/mocks)
curl -s http://localhost:8080/mock/petstore/v1/pets/42

# MCP: start a session (2025-06-18 handshake)
curl -si http://localhost:8080/mcp -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'

# A2A: the default agent card
curl -s http://localhost:8080/.well-known/agent-card.json

# Chaos on any route
curl -si http://localhost:8080/echo -H 'X-Rustybin-Fail: 503'

# An access token from the built-in identity provider
curl -s http://localhost:8080/oauth/token \
  -d grant_type=client_credentials -d client_id=rustybin -d client_secret=secret
The same requests as a tested Hurl file
GET {{base_url}}/anything/hello?demo=1
X-Demo: rustybin
HTTP 200
[Asserts]
jsonpath "$.path" == "/anything/hello"
jsonpath "$.headers['x-demo'][0]" == "rustybin"

POST {{base_url}}/ai/openai/v1/chat/completions
Content-Type: application/json
{"model": "gpt-4o", "messages": [{"role": "user", "content": "hello"}]}
HTTP 200
[Asserts]
jsonpath "$.choices[0].message.content" startsWith "Hello! I'm Rustybin"

GET {{base_url}}/mock/petstore/v1/pets/42
HTTP 200
[Asserts]
header "X-Rustybin-Mock-Operation" == "getPetById"
jsonpath "$.id" == 42

POST {{base_url}}/mcp
Accept: application/json, text/event-stream
Content-Type: application/json
{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "curl", "version": "1"}}}
HTTP 200
[Asserts]
header "Mcp-Session-Id" exists
jsonpath "$.result.serverInfo.name" == "rustybin-mcp"

GET {{base_url}}/.well-known/agent-card.json
HTTP 200
[Asserts]
jsonpath "$.name" == "Echo Agent"

GET {{base_url}}/echo
X-Rustybin-Fail: 503
HTTP 503

POST {{base_url}}/oauth/token
[Form]
grant_type: client_credentials
client_id: rustybin
client_secret: secret
HTTP 200
[Asserts]
jsonpath "$.token_type" == "Bearer"

Then open http://localhost:8080/ for the endpoint list with runnable examples, /docs for the interactive API reference and /ui for the web console.