# deploy/Caddyfile — the edge config for rutster-edge (T2) + rutster-allinone (T1).
# Same file, same build (spec §3.2 + ADR-0011). Read by Caddy v2.8 (the
# custom xcaddy build from deploy/Dockerfile).
#
# Env vars (Caddyfile interpolation syntax {$VAR}):
#   RUTSTER_DOMAIN      — the public hostname (e.g. pbx.example.com). Required.
#   RUTSTER_ACME_EMAIL  — the Let's Encrypt account email. Required for ACME.
#   RUTSTER_LOCAL_CERTS — set to "true" in CI to use Caddy's internal CA
#                         (no ACME round-trips, no rate-limit budget burns).
#                         Overrides the ACME block below via the `local_certs`
#                         global option.
#
# Timeouts (TLS brief §3(a); spec §2.1, §2.2, §3.1 invariant 3):
#   * read_body 30s      — Twilio webhooks fit comfortably in 30s (15s cap
#                          by invariant 6).
#   * read_header 30s    — same.
#   * write 0            — disable; 20ms media frames must flush immediately.
#   * idle 24h           — the universal 60s-class default kills quiet WS;
#                          spec §2.1 invariant 3 sets max call duration 24h.
#   * stream_close_delay 24h — above max call duration; the load-bearing
#                              mitigation for caddy #6420/#7222 (TLS brief §5
#                              risk 1; spec §9 reload-during-call smoke).
#
# X-Forwarded-* honesty (spec §3.1 invariant 5; plan-A Task 5
# reconstruct_public_url): Caddy sets these to the real client values via
# the `header_up` directives below. Engine-side RUTSTER_TRUSTED_PROXIES is
# the trust gate (fail-closed default).

{
	# CI override: set RUTSTER_LOCAL_CERTS=true to use Caddy's internal CA
	# (no ACME in CI; spec §9). Production leaves this unset.
	{$RUTSTER_LOCAL_CERTS:local_certs_off} local_certs

	# Send logs to stdout (s6-overlay / docker logs collects from there).
	log {
		output stdout
		format console
	}
}

# Default ACME email — overridden by RUTSTER_LOCAL_CERTS=true in CI.
{
	email {$RUTSTER_ACME_EMAIL:you@example.com}
}

# Main site block — the public hostname.
{$RUTSTER_DOMAIN:localhost} {
	encode zstd gzip

	# The reverse_proxy upstream. Caddy env-substituted form
	# {$RUTSTER_UPSTREAM:127.0.0.1:8080} — preserves the T1 loopback
	# default (single container) AND lets T2 compose override via the
	# engine service's `RUTSTER_UPSTREAM: engine:8080` env entry. (Pre-T6
	# the upstream was a hardcoded 127.0.0.1:8080 literal — fine in T1,
	# wrong in T2 where Caddy reaches the engine on the compose network
	# via the `engine` DNS name, not loopback.) Same Caddyfile, two netns
	# realities.
	#
	# `header_up Host {host}` + X-Forwarded-* — sets the honest hop values
	# (the engine's reconstruct_public_url reads these; spec §3.1 invariant 5).
	# `header_up X-Forwarded-For {remote_host}` — included for completeness;
	# the engine does NOT use X-Forwarded-For for signature validation, only
	# X-Forwarded-Proto/Host. Forwarding the For header is standard proxy
	# hygiene — engine-side RUTSTER_TRUSTED_PROXIES is the gate.
	reverse_proxy {$RUTSTER_UPSTREAM:127.0.0.1:8080} {
		header_up Host {host}
		header_up X-Real-IP {remote_host}
		header_up X-Forwarded-Proto {scheme}
		header_up X-Forwarded-Host {host}
		header_up X-Forwarded-For {remote_host}

		# Tuned timeouts — the load-bearing ones for calls lasting hours.
		transport http {
			read_buffer 64KiB
			write_buffer 64KiB
			dial_timeout 5s
			response_header_timeout 30s
		}

		# stream_close_delay above max call duration (24h) — the caddy
		# #6420/#7222 mitigation (TLS brief §5 risk 1; spec §9 smoke).
		# An operator Caddyfile reload mid-call would normally kill
		# upgraded WS tunnels; the delay keeps them alive until the
		# streams close naturally (the call ends) — verified by the
		# reload-during-live-call CI smoke (Task 9).
		stream_close_delay 24h
	}

	# Timeouts for the public-facing side.
	servers {
		read_body 30s
		read_header 30s
		write 0        # never write-timeout; 20ms frames self-flush
		idle 24h       # above max call duration — the universal 60s-class default kills quiet WS
	}
}
