Context
No cross-region replication exists or is buildable for the 14.6 TiB ClickHouse lake, and a per-region S3-tiered lake fails on cost, latency and R3's disk. ADR-0008 had ruled multi-region active/active out of v1 and ADR-0016's Model A (R1-canonical Postgres replication) does not exist. The full plan, cost model and phasing live in docs/architecture/ha-plan.md; implementation is deferred until after v1.0 (plan §10), so the build is not in the tree yet.
Decision
Section labels match the plan doc and are cited from code.
- Model B: independent per-region ingest. Each region ingests the chain itself and builds its own stores; consistency is by determinism (ADR-0015), not replication. No cross-region Postgres replication and no stretched Patroni cluster.
- §3a: pricing and oracle tier is active/active in every region on local Timescale and Redis. The ADR-0009 SLO holds because no SLO'd route crosses a region boundary.
- §3b: the lake and explorer tier is R1-authoritative. R2 and R3 are the same shape: local pricing, Redis and API, with lake routes proxied to R1 and Cloudflare R2 as fallback only. Per-region S3-tiered ClickHouse is rejected.
- §3c: control-plane state (accounts, keys, sessions, webauthn, alerts, webhooks) needs real cross-region replication as its own workstream; until it lands only anonymous traffic fails over cleanly.
- §3d (the rate-limit amendment): rate limit and monthly quota count per region, in the serving region's own Redis, keyed per principal with no region dimension, so under active/active the global ceiling is N regions times the limit and a Redis outage fails closed in that region only. The mitigation is an open decision due before a second region serves authenticated traffic; a synchronous cross-region counter is excluded for the rate limit because it would put a cross-region round trip on every request.
- §4: per-region shapes differ in storage but serve byte-identical closed-bucket answers (ADR-0016's surviving principle). R2 moves off AWS to US bare metal.
- §5: durability. The raw galexie-archive is a copy of SDF's public dataset and is re-pulled from there, not mirrored off-site (
galexie_archive_mirror_enableddefaults to false). Off-site copies are the ClickHouse lake and Postgres on Backblaze B2 (ADR-0043). - §7.3: lake-aware health.
GET /v1/livez/lakeis the load-balancer probe for lake-backed routes;/v1/readyzcovers the pricing tier. - HA is cross-region failover with one box per region; ADR-0008's single-region HA topology and DR principle carry forward as Phase 1 (HAProxy, Patroni, Sentinel fleets, deferred post-v1.0); the multi-region layer adds failover between single-box regions, never a stretched cluster.
- Determinism hardening is a launch gate for multi-region (plan §7.2): three independent regions must return equal answers.
Invariant
- SLO'd handlers never read lake-backed fields or cross a region boundary:
TestSLORoutesNeverTouchTheLakeininternal/api/v1/slo_guard_test.go. /v1/livez/lakeshares one ping per round, fails closed when the lake is absent and never echoes the driver error:internal/api/v1/livez_lake_test.go.- The rate limiter and monthly quota keep per-principal Redis counters with no region dimension:
internal/api/v1/middleware/ratelimit.go,internal/api/v1/middleware/monthly_quota.go.
Consequences
- ADR-0008's single-region HA topology and DR principle carry forward; only its multi-region decision is overturned, and ADR-0016 is superseded.
- the multi-region topology doc is deleted;
r2-r3-bringup.mdandmulti-region-cutover.mddescribe rejected architectures and are banner-marked; implement from the plan doc. - ADR-0044 explorer edge rendering is the enabler for runtime cross-region explorer failover.
- Lake failover is fast when R1 is healthy and degraded during an R1 outage; fleet cost is about $15-18K/yr against $180-288K/yr for per-region HA fleets.
- Until a second region serves authenticated traffic the published limits are exact (N = 1).
Evidence
docs/architecture/ha-plan.md, internal/api/v1/slo_guard_test.go, internal/api/v1/livez_lake_test.go, internal/api/v1/server.go (/v1/livez/lake route).