carlos / platform Public

docs: three corrections to the #277 amendment (review)

- Aliases: the widened set is exactly-named registry ROUTES.
  edgesFilePass publishes reg.List(), so an attached custom domain (an
  alias row) is in no edges file on any box and KnownHost is false for
  it everywhere. Stated as its own residual, with the constraint a
  future alias-publishing change inherits: a *.parent alias stays
  wildcard-cert-only.
- Order amplification: the set of names a stranger can induce a fresh
  order for grows from one box's routes to every box's routes, each
  dead-DNS order burning that hostname's failed-validation budget and a
  slot of the shared account's new-order rate. Pre-existing class,
  amplified by deployment size, inside the accepted envelope — said out
  loud rather than left for a reader to derive.
- Publish cadence: 30s is the READER's poll; the publisher is 5min + up
  to 1min jitter, so a new route is admissible on remote edges after
  ≈6.5 minutes worst case. Safe, and now explained: Home() misses the
  same name over the same window, so the non-home edge 302s instead of
  backhauling and no order starts — a routing wait, not an LE burn.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 53d221e979422101dda183ba8c48a7eb54d6e4f6 parent ca44834
1 file changed, +53 −14
  • docs/superpowers/specs/2026-08-20-multi-region-ingress-design.md +53 −14
diff --git a/docs/superpowers/specs/2026-08-20-multi-region-ingress-design.md b/docs/superpowers/specs/2026-08-20-multi-region-ingress-design.md
index 4d3425d0b..246e84bf6 100644
--- a/docs/superpowers/specs/2026-08-20-multi-region-ingress-design.md
+++ b/docs/superpowers/specs/2026-08-20-multi-region-ingress-design.md
@@ -186,12 +186,14 @@ in the dev topology).
All three gates now admit a host when it is in this box's registry
(`Allowed`, unchanged) **or** is an **EXACT** key of the mirrored home
map (`homeMap.KnownHost`, folded from the same `control/edges/**` objects
-Part 3 already mirrors on the same 30s cadence) — bridged in one place,
-`Edge.deploymentHost`. Both halves stay registry-backed and
+Part 3 already mirrors, read on the reader's 30s poll) — bridged in one
+place, `Edge.deploymentHost`. Both halves stay registry-backed and
deployment-controlled: edges files are written by each box's own loop
from its own registry, so a stranger cannot add a name without a control-
bucket write, which Part 2's trust statement already treats as
-game-over.
+game-over. The widened set is exactly-named registry **routes**:
+`edgesFilePass` publishes `reg.List()`, which is the routes table and
+nothing else.
**The one-label wildcard rule stays excluded, and that is load-bearing.**
The home map carries `*.<parent>` catch-all rows verbatim, and `Home()`'s
@@ -211,17 +213,42 @@ pinned by test on all three surfaces
`TestALPNReadThroughRefusesWildcardLabelUnderRemoteCatchAll`,
`TestChallengeReadThroughStillRefusesStrangers`).
-*Residual, filed rather than fixed.* An order still only ever STARTS on
-the issuer, because autocert orders lazily out of `GetCertificate`. In a
-3-plus-edge deployment where DNS points at neither the home box nor the
-issuer, no hello ever reaches the issuer and the name simply stays
-certless — **failing closed, with zero LE spend** (a non-issuer answers
-`errNotIssuer` before autocert's cache lookup, so backhauled hellos there
-order nothing). Steering heals the steered case (its latency records
-include the issuer edge) and the custom-domain CNAME guidance covers the
-rest; a proactive issuer-side order nudge for exact home-map names is the
-natural fix and is a follow-up, not an overnight change. Issue #277
-carries this ruling and the residual.
+*Residuals, filed rather than fixed.* Three, none of them blocking.
+
+**The order trigger stays on the issuer.** autocert orders lazily out of
+`GetCertificate`, so in a 3-plus-edge deployment where DNS points at
+neither the home box nor the issuer, no hello ever reaches the issuer and
+the name simply stays certless — **failing closed, with zero LE spend** (a
+non-issuer answers `errNotIssuer` before autocert's cache lookup, so
+backhauled hellos there order nothing). Steering heals the steered case
+(its latency records include the issuer edge) and the custom-domain CNAME
+guidance covers the rest; a proactive issuer-side order nudge for exact
+home-map names is the natural fix and is a follow-up, not an overnight
+change.
+
+**Attached custom domains are NOT covered.** A custom domain attached to
+an app that already has a ready route lands in `route_aliases`
+(`internal/console/domainattach.go`'s alias branch,
+`internal/domains/aliases.go`), never in the routes table — so it appears
+in no edges file and `KnownHost` is false for it on every box. Such a name
+is admissible only where it is an alias in that box's OWN registry
+(`Allowed` already resolves alias rows), which is unchanged from today.
+Publishing aliases into edges files is the obvious extension and is a
+follow-up issue, with one constraint stated in advance: an alias whose
+host is a `*.<parent>` catch-all must stay wildcard-cert-only if it is
+ever published — the same #180 bound this amendment refuses to widen.
+
+**Order amplification, accepted.** The set of names a stranger can induce
+a fresh ACME order for at the issuer grows from one box's routes to every
+box's routes. Each such order for a name whose DNS is dead burns that
+hostname's LE failed-validation budget and a slot of the shared account's
+new-order rate. This is a pre-existing class — any routed name has always
+been inducible on the box that homes it — amplified by deployment size
+rather than introduced here, and it is inside this ruling's accepted
+envelope: the alternative is leaving home-boxed routes unable to mint at
+all.
+
+Issue #277 carries this ruling and all three residuals.
*Rollout.* Read-side only — no wire or storage shape, no new writer, no
knob — and dormant on single-box deployments, where the fold is empty and
@@ -231,6 +258,18 @@ the refusing validator #277 describes. Until then, do not point real DNS
at a non-home edge for a home-boxed name; that hour-long rate-limit has
already been paid once.
+*A new route is not admissible instantly, and that is safe.* The 30s
+figure is the READER's poll; the publisher is `edgesFileLoop`'s 5min +
+up-to-1min jitter (`internal/host/edgesfile.go`), so a freshly created
+route can take ≈6.5 minutes worst case to become admissible on remote
+edges. During that window `Home()` misses the name too — the two read the
+same fold — so a non-home edge does not backhaul it: it answers the
+ordinary `unknownHost` 302 (or 404), no hello reaches the home box, and
+no order starts. The cost is a routing wait, not an LE burn. The one
+exception is a hello that reaches the issuer another way (a steered
+client, an operator's `--resolve`) while the issuer's own registry
+already has the row — which is simply today's single-box behaviour.
+
**Issuer death**: the lease expires and the next box's housekeeper takes
over with identical machinery. Distributed certs keep every edge serving
through the gap; a dead issuer costs nothing until the earliest renewal