spec: canary minting is static-only; durable account ids gate the rollout
Merge review found that a minted canary host for an instance app would proxy to the stable process while advertising the canary channel — a URL that lies. Instance canaries need per-host instance spawning, not routing aliases. Also records two prerequisites the review surfaced: durable account identity (sqids currently ride reassignable row ids) and wake-through aliasing for hibernating apps.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 file changed,
+32
−4
docs/superpowers/specs/2026-08-02-custom-domains-design.md+32 −4
diff --git a/docs/superpowers/specs/2026-08-02-custom-domains-design.md b/docs/superpowers/specs/2026-08-02-custom-domains-design.md| index 58c906a36..023f1a233 100644 |
| --- a/docs/superpowers/specs/2026-08-02-custom-domains-design.md |
| +++ b/docs/superpowers/specs/2026-08-02-custom-domains-design.md |
| @@ -58,10 +58,20 @@ each step is idempotent and additive: |
| (Socket/Addr/backing) as the app's first ready route (ordered by host, |
| deterministic), `platform=1`. |
| Existing apps are backfilled on the first pass after rollout. |
| -3. **Canary hosts**: any `canary/<name>` channel pointer in the bucket |
| - whose app has no route following it gets |
| - `<name>.<app>.<sqid>.<apps-domain>` minted on that channel — closing |
| - console slice 3's hostless-canary gap with no console write authority. |
| +3. **Canary hosts — static apps only** (corrected 2026-08-02 after merge |
| + review): any `canary/<name>` channel pointer whose app has no route |
| + following it gets `<name>.<app>.<sqid>.<apps-domain>` minted on that |
| + channel **when the app's source route is `Kind=static`** — there the |
| + copied channel genuinely IS the backend (the static handler resolves |
| + the pointer to a tar), so the URL serves what it claims. |
| + **Instance/service apps are skipped**: a minted route can only copy an |
| + existing upstream, so a canary URL would proxy to the app's *stable* |
| + process while advertising the canary channel — a URL that lies about |
| + what it serves, and an operator could validate a build that isn't |
| + running. Closing that gap needs per-host canary *instance spawning* |
| + (the unbuilt `carlos canary` verb), not a routing alias. Console slice |
| + 3's hostless-canary gap therefore remains open for instance apps, by |
| + design, until that lands. |
| Retiring the channel (console or CLI) removes the minted route on a |
| later pass (only routes with `platform=1` are ever auto-removed; |
| custom domains are never touched by reconciliation). |
| @@ -143,6 +153,24 @@ Per app page, replacing the raw routes framing: |
| deliberately excluded here). |
| - **Multi-region DNS checks** (per-region edge IP sets once the mesh |
| ports land). |
| +- **Durable account identity** (raised by merge review, 2026-08-02): |
| + sqids currently encode the account's AUTOINCREMENT registry row id, |
| + assigned in bucket-listing order at first reconcile. A registry rebuild |
| + — or a second box materializing accounts in a different order — |
| + reassigns ids and silently re-homes every platform domain. The frozen |
| + goldens freeze the *encoding*, not the *assignment*. Before these URLs |
| + are shared anywhere that matters, account identity must become durable: |
| + either the account's assigned id is written back into its bucket |
| + manifest (the bucket is the control plane, so it should hold the |
| + identity) or the sqid derives deterministically from the account name. |
| + **This is a prerequisite for enabling the feature on the flagship**, |
| + and belongs in the multi-account spec that supersedes §2. |
| +- **Wake-through aliasing**: a hibernating app's platform domain 502s |
| + while the app sleeps — the activator arms on the canonical route only, |
| + and a minted route deliberately does not copy `Hibernate` (copying it |
| + would spawn a duplicate process against an empty DBPath). The edge |
| + needs to resolve an alias to its source route before the activator |
| + gate for "every app is born addressable" to hold for sleepers. |
| - **Path routing** — one host fanning out to several upstreams by path |
| prefix: `/blog` to a static site while the rest of the domain serves |
| the dynamic app, or an app's satellites behind one origin (Woodstar's |