deploy slice B + F: deploy/ artifacts + image-build + container smoke CI + tag-push publish #26

Merged
alee merged 11 commits from deploy-b/packaging-ci into docs/deployment-topology-spec 2026-07-06 19:53:33 +00:00
Showing only changes of commit 1d007d99c6 - Show all commits

148
deploy/.env.example Normal file
View File

@@ -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<String>` 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