# deploy/.env.example — runtime configuration for the rutster T1 (all-in-one) # + T2 (compose) artifacts. Copy to `.env` + edit before `docker compose up -d`. # # Format: KEY=value (one per line); comments (#) are ignored by `docker # compose --env-file`. Fail-fast pattern: every var is parsed by # crates/rutster/src/config.rs's pure-`Option` parsers at startup; # an invalid value fails the boot loudly with the var name in the error, # never silently runs with a wrong default. See AGENTS.md "config.rs" # pattern. # # Bundled-binary licenses (aggregation-clean vs GPL-3 per ADR-0004): # * Caddy Apache-2.0 # * valkey BSD-3 # * s6-overlay ISC # * libopus0 BSD-3 (system package; Debian) # Future cargo-deny-adjacent image license scan will assert these in CI # (spec §6.1 — out of scope for slice B; this comment seeds it). # # Registry namespace: git.adlee.work/alee/rutster-* (matches the `alee` tea # login per AGENTS.md Git workflow + the root Cargo.toml's `repository` # field). If your Gitea registry uses a different owner segment, replace # `alee` throughout deploy/compose.yaml + .github/workflows/publish-images.yml. ################################################################################ # ARTIFACT-LEVEL — Caddy edge (this slice) ################################################################################ # RUTSTER_DOMAIN — the public hostname the CPaaS dials. Required for T1 (solo # all-in-one) and T2 (compose). Used by Caddyfile as the site block name. # Example: pbx.example.com RUTSTER_DOMAIN=pbx.example.com # RUTSTER_ACME_EMAIL — the Let's Encrypt account email. Used by Caddy for # ACME account creation. Required for production (ACME issuance); ignored # in CI (RUTSTER_LOCAL_CERTS=true bypasses ACME). RUTSTER_ACME_EMAIL=you@example.com # RUTSTER_LOCAL_CERTS — set to "true" in CI to use Caddy's internal CA (no # ACME round-trips, no rate-limit budget burns). Production leaves this # unset (or "false") so Caddy uses Let's Encrypt. The Caddyfile reads this # via the {$RUTSTER_LOCAL_CERTS:local_certs_off} interpolation. # RUTSTER_LOCAL_CERTS=false ################################################################################ # FOB engine — bind + drain (slice-5 config.rs + plan-A) ################################################################################ # RUTSTER_HTTP_BIND — the FOB's HTTP/WS listener. Behind Caddy (T1/T2 prod): # 0.0.0.0:8080 (compose network) or 127.0.0.1:8080 (single container T1). # Default if unset: 0.0.0.0:8080 (config.rs http_bind). RUTSTER_HTTP_BIND=0.0.0.0:8080 # RUTSTER_DRAIN_DEADLINE_SECS — how long shutdown waits for in-flight calls # before the hard stop. Default 0 = instant shutdown (dev loop); production # sets minutes. Spec §2.2 pins 600 (paired with compose stop_grace_period # 660s — grace MUST exceed drain or the orchestrator kills calls the # engine was gracefully draining). RUTSTER_DRAIN_DEADLINE_SECS=600 # RUTSTER_MAX_SESSIONS — admission cap. Default 64 (placeholder until the # ADR-0010 benchmark measures the real per-node ceiling). /readyz flips 503 # when at the cap so the LB pulls the node. RUTSTER_MAX_SESSIONS=64 ################################################################################ # FOB engine — WebRTC media (slice-5 config.rs media_address_config) ################################################################################ # RUTSTER_MEDIA_BIND_IP — the IP WebRTC DTLS-SRTP binds. Default 127.0.0.1 # (loopback — dev loop). Production hosts a public IP. RUTSTER_MEDIA_BIND_IP=0.0.0.0 # RUTSTER_MEDIA_ADVERTISED_IP — the IP advertised in SDP to WebRTC callers. # They send media UDP straight to this IP (NAT 1:1 — no STUN, no TURN). # In T1 (host networking): the host's public IP. In T2 (compose): the # engine container's published port's host IP. RUTSTER_MEDIA_ADVERTISED_IP=203.0.113.7 # RUTSTER_MEDIA_PORT_RANGE — UDP port range for WebRTC media. Format lo-hi # inclusive. Must be open inbound on the host's firewall + published in # compose (deploy/compose.yaml publishes this range). RUTSTER_MEDIA_PORT_RANGE=49152-49407 ################################################################################ # FOB engine — WS hygiene (plan-A §5.2) ################################################################################ # RUTSTER_WS_PING_SECS — engine-originated app-level WS ping interval on # the trunk media-stream WS. Default 20s. 0 rejected (would busy-loop the # ping timer); no "off" spelling by design. RUTSTER_WS_PING_SECS=20 ################################################################################ # FOB engine — trusted-proxy posture (plan-A §5.3) ################################################################################ # RUTSTER_TRUSTED_PROXIES — comma-separated CIDR list of edge peers whose # X-Forwarded-Proto/Host are believed. Empty or unset = headers IGNORED # (fail-closed default). Bare IPs accepted as host-length prefixes. In T1/T2 # with Caddy as edge, set to the Caddy container's source IP (compose: # the compose network's subnet CIDR; T1: 127.0.0.1/32). An internet peer # must never choose the public URL that Twilio signature validation # reconstructs (spec §3.1 invariant 5). RUTSTER_TRUSTED_PROXIES=127.0.0.1/32 ################################################################################ # Trunk — Twilio credentials (slice-5 config.rs twilio_credentials) ################################################################################ # All four RUTSTER_TWILIO_* vars are ALL-OR-NONE: the parser rejects partial # config at startup. Omit all four to run WebRTC-only. RUTSTER_TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx RUTSTER_TWILIO_AUTH_TOKEN=your_auth_token_here RUTSTER_TWILIO_MEDIA_BIND=0.0.0.0:8081 RUTSTER_TWILIO_WEBHOOK_BASE=https://pbx.example.com ################################################################################ # Brain — tap URL (slice-2/3; loopback-only until step 6) ################################################################################ # RUTSTER_TAP_URL — the WS URL the FOB opens to the brain. Default # ws://127.0.0.1:8081/echo (slice-2 dev-loop echo brain). Production with # rutster-brain-realtime: ws://127.0.0.1:8082 (loopback-only — # resolve_tap_url rejects non-loopback until step 6's wss:// tap). In T2 # (compose), the brain shares the engine's netns # (network_mode: "service:engine") so this loopback IS the engine's # loopback. RUTSTER_TAP_URL=ws://127.0.0.1:8082 ################################################################################ # Future vars — land with sibling slices, NOT this slice ################################################################################ # RUTSTER_TLS_CERT / RUTSTER_TLS_KEY — land with slice C (rustls Phase 1, # BYO-cert in-process TLS). The engine binary stays compiled-with-rustls # from slice C onward; runtime-gated by these vars. See the slice-C plan # at docs/superpowers/plans/2026-07-05-deploy-c-rustls-tls.md (path may # vary — check the plans dir). # RUTSTER_TLS_CERT=/etc/rutster/tls/fullchain.pem # RUTSTER_TLS_KEY=/etc/rutster/tls/privkey.pem # RUTSTER_METRICS_BIND — lands with slice D (/metrics endpoint). Default # 127.0.0.1:9090 (internal — never routed through Caddy). # RUTSTER_METRICS_BIND=127.0.0.1:9090 # RUTSTER_VALKEY_URL — lands with slice E (ValkeyEventSink). Unset = today's # TracingEventSink (logs to stdout). Set to redis://valkey:6379/0 in T2. # RUTSTER_VALKEY_URL=redis://valkey:6379/0