From 1d007d99c61741dfd0ea379d5f507991f429be24 Mon Sep 17 00:00:00 2001 From: "Aaron D. Lee" Date: Sun, 5 Jul 2026 22:46:04 -0400 Subject: [PATCH] =?UTF-8?q?docs(deploy):=20.env.example=20=E2=80=94=20all?= =?UTF-8?q?=20RUTSTER=5F*=20vars=20documented=20(deploy-B=20=C2=A76.2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The operator documentation surface. Lists every RUTSTER_* var with its default + the slice that introduced it + the fail-fast behavior. Includes: * Artifact-level (this slice): RUTSTER_DOMAIN, RUTSTER_ACME_EMAIL, RUTSTER_LOCAL_CERTS (CI override). * FOB engine (slice-5 + plan-A): RUTSTER_HTTP_BIND, RUTSTER_DRAIN_DEADLINE_SECS=600 (paired with compose stop_grace_period 660s per spec §2.2 — grace exceeds drain), RUTSTER_MAX_SESSIONS, RUTSTER_MEDIA_BIND_IP/ADVERTISED_IP/PORT_RANGE, RUTSTER_WS_PING_SECS, RUTSTER_TRUSTED_PROXIES. * Trunk (slice-5): the four RUTSTER_TWILIO_* (all-or-none — config.rs twilio_credentials rejects partial). * Brain (slice-2/3): RUTSTER_TAP_URL (loopback-only until step 6). * Future var stubs: RUTSTER_TLS_CERT/KEY (slice C), RUTSTER_METRICS_BIND (slice D), RUTSTER_VALKEY_URL (slice E) — commented out with a pointer to the sibling plan. Bundled-binary licenses noted in the header comment (Caddy Apache-2.0, valkey BSD-3, s6-overlay ISC, libopus0 BSD-3 — aggregation-clean vs GPL-3 per ADR-0004); seeds the future cargo-deny-adjacent image license scan (spec §6.1, out of scope for this slice). Registry namespace git.adlee.work/alee/rutster-* + the 'if your Gitea registry uses a different owner segment, replace alee throughout' note. Signed-off-by: Aaron D. Lee --- deploy/.env.example | 148 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 deploy/.env.example diff --git a/deploy/.env.example b/deploy/.env.example new file mode 100644 index 0000000..a0c6b31 --- /dev/null +++ b/deploy/.env.example @@ -0,0 +1,148 @@ +# 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