Critical findings resolved: - C1: Site routing is global (per-listener TOML, global runtime lookup) - C2: X-Forwarded-For replaces (not appends) — edge proxy model (ADR-021) - C3: Hop-by-hop header handling rules specified (proxy.md) - C4: ACME failure behavior defined (tls.md) - C5: Startup sequence with fail-fast semantics (operations.md) - C6: Per-listener Router instances with shared global state (overview.md) - C7: Rate limiter adopts new params on next request, no state clear (operations.md) Warnings resolved: - W1: Admin socket wire protocol specified - W2: Host header port stripped, hostnames only in config - W3: HTTP redirect URL construction with port handling - W4: /health on HTTPS matches regardless of Host header - W5: Static config changes logged as warning during reload - W6: Reload operations serialized via Mutex - W7: http_port validation rules added (9 new rules total) - W8: upstream format validation (host:port required, no scheme) - W9: TLS error handling table (SNI, version, cipher failures) - W10: IPv6 rate limited per /64 prefix - W11: Graceful shutdown sequence specified (6 steps) - W12: Error response bodies: minimal plain text, no version disclosure - W13: upstream_scheme HTTPS uses system CA store - W14: allow_wildcard_bind is OR between config and CLI - W15: ADR-010 Phase 2 list updated (timeouts moved to Phase 1) - W17: LoggingConfig static/restart note added Suggestions applied: - S2: ConnectInfo propagation note - S3: Case-insensitive host matching (RFC 7230) - S5: Response streaming behavior (chunk-by-chunk) - S6: Token bucket nodelay semantics - S7: File watching explicitly out of scope - S8: All paths forwarded without filtering - S9: shutdown_timeout_secs referenced in shutdown description - S11: Consolidated defaults table in config.md
3.3 KiB
ADR-010: Multi-Site Support in Phase 1
Status
Accepted
Context
The original architecture phased multi-site support into Phase 2, treating
Phase 1 as a single-domain replacement for nginx serving only git.alk.dev.
This was based on the assumption that only one domain needed proxying initially.
However, alk.dev (the bare domain) will need proxying in the near future.
While alk.dev is a simple case — proxying to a Deno/Fresh container with no
special requirements — the proxy must support multiple sites from day one. The
config format, routing logic, and TLS certificate provisioning all need
multi-site awareness.
Additionally, api.alk.dev is explicitly out of scope (it runs its own
HTTP/2+ server natively), but the proxy must not prevent future sites from
being added.
The cost of deferring multi-site is high: we'd need a config format migration,
routing logic rewrite, and TLS cert management changes later. Supporting
multi-site from the start costs very little — the config format just uses an
array of sites (which it already does), host-based routing is trivial in axum,
and rustls-acme supports multi-domain certificates natively.
Decision
Move multi-site support from Phase 2 into Phase 1. The proxy supports multiple sites from the initial release:
[[listeners.sites]]array in each listener config (after ADR-019; was[[sites]]at top level)- Host-based routing via axum's
Hostextractor (already the planned approach) - Multi-domain ACME certificate provisioning via
rustls-acme - Each site maps a hostname to an upstream address
Phase 1 scope becomes:
- Multi-site reverse proxy with TLS termination
- ACME certificate management (multi-domain)
- HTTP → HTTPS redirect
- Rate limiting, logging, health check, graceful shutdown
- Systemd integration
Phase 2 scope shifts to operational hardening:
- Per-site rate limits and body limits
- Metrics endpoint (Prometheus-compatible)
- Connection limits and timeouts
- Log rotation
Note: "Per-site upstream timeouts" was originally listed here but was moved to Phase 1 via ADR-015.
Phase 3 remains future enhancements.
Rationale
- The config format already uses
[[sites]]— no format change needed - Host-based routing is the natural axum pattern and was already planned
rustls-acmeacceptsVec<domain>— multi-domain is its default usage- The cost of adding multi-site later (config migration, routing rewrite, cert management changes) far exceeds the cost of supporting it now (zero additional complexity)
alk.devis confirmed as a near-term need, not a hypothetical- The proxy's value proposition is being a memory-safe reverse proxy for our infrastructure, which has multiple domains
Consequences
Positive:
- No config format migration needed later
alk.devcan be added to the config without code changes- TLS cert management handles multiple domains from the start
- Eliminates an entire phase of work
Negative:
- Slightly more testing surface (must verify correct routing with multiple sites)
- Must test multi-domain ACME provisioning (not just single-domain)
- Wildcard or fallback site behavior is defined by the listener's site routing
References
- overview.md
- config.md
- tls.md
- proxy.md
- ADR-002 (custom proxy handler — rationale updated for multi-site)