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
92 lines
3.3 KiB
Markdown
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) |