Files
reverse-proxy/docs/architecture/decisions/010-multi-site-phase1.md
glm-5.1 ceb59ad9b9 Resolve all architecture review findings (7 critical, 14 warnings, 6 suggestions)
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
2026-06-11 10:56:40 +00:00

92 lines
3.3 KiB
Markdown

# 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 `Host` extractor (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:
1. Multi-site reverse proxy with TLS termination
2. ACME certificate management (multi-domain)
3. HTTP → HTTPS redirect
4. Rate limiting, logging, health check, graceful shutdown
5. Systemd integration
Phase 2 scope shifts to operational hardening:
1. Per-site rate limits and body limits
2. Metrics endpoint (Prometheus-compatible)
3. Connection limits and timeouts
4. 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-acme` accepts `Vec<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.dev` is 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.dev` can 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](../overview.md)
- [config.md](../config.md)
- [tls.md](../tls.md)
- [proxy.md](../proxy.md)
- ADR-002 (custom proxy handler — rationale updated for multi-site)