| index e819bf46f..80af54a52 100644 |
| --- a/docs/superpowers/specs/2026-08-12-woodstar-carloku-migration-design.md |
| +++ b/docs/superpowers/specs/2026-08-12-woodstar-carloku-migration-design.md |
| @@ -162,8 +162,18 @@ Mechanics (all provider-blind, the `dnsProvider` seam is untouched): |
| |
| - `wildcard.go` computes `challengeFQDN = _acme-challenge.<parent>` |
| today. Delegated parents get a parent → delegation-target mapping; |
| - `solveDNS01` publishes at the target. `waitForTXT` polls the original |
| - name — resolvers follow the CNAME, so it already works. |
| + `solveDNS01` publishes at the target. **`waitForTXT` polls the |
| + delegation target, not the original name** (amended per final |
| + review): we confirm the write we control, and the customer zone's |
| + negative-cache TTL is not ours to bound. Consequence, stated: |
| + `waitForTXT` is NOT a delegation-setup check — a missing customer |
| + CNAME sails through the wait and fails at Let's Encrypt, one failed |
| + validation per parent per hour (inside LE limits); the attach-surface |
| + CNAME gate below is the real setup check. |
| +- **Platform parents always win** (final-review ruling): a delegated |
| + entry colliding with a static domain or sitting under the apps |
| + domain is dropped at `refresh()`, never preferred — one bad claim |
| + must not degrade a live platform wildcard's renewal. |
| - **Declaration — two additive claim fields** (the #82 session's |
| catch: one bool must not fuse two orthogonal capabilities): |
| - `wildcard: true` — this domain is a **delegated wildcard parent |
| @@ -249,7 +259,9 @@ world expect Caddy's path fan-out on one origin. Fix is relay-side |
| `-notify-upstream`, `-unfurl-upstream`, `-apbridge-upstream` on |
| `cmd/relay`. When set, the relay reverse-proxies: |
| `/media` + `/media/*` → media (including `POST /media` uploads); |
| - `/api/*` → indexer; `/stream/*` → notify; `/unfurl*` → unfurl; |
| + `/api/*` → indexer; `/stream/*` → notify; `/unfurl*` AND `/img*` → |
| + unfurl (`/img` serves every link-card image — omitting it fails as a |
| + 200 SPA page inside `<img>` tags, caught in final review); |
| `/.well-known/webfinger`, `/.well-known/host-meta`, `/users/*`, |
| `/inbox`, `/ap/*` → apbridge — the devbox Caddyfile's route set, |
| which is the truthful superset of prod's. `httputil.ReverseProxy` |
| @@ -317,6 +329,13 @@ units, data, routes. |
| |
| **Cutover (HELD for Paul's explicit go; quiet hour; TTLs already 300):** |
| |
| +Pre-flight invariant (final-review catch): **`status.woodstar.app`'s |
| +exact-host route MUST exist before the wildcard alias serves** — with |
| +the alias live and the status route missing, status requests 302 to |
| +`woodstar.app/status` (an SPA client route) and the status page |
| +silently becomes the app. Exact-host beats wildcard; verify the |
| +precedence in rehearsal. |
| + |
| 1. Old box (SSH): stop the six `woodstar-*` units → run one final |
| media-backup pass → wait ~30 s for litestream's final WAL frames → |
| stop litestream → copy `apbridge.seed` off the box → verify newest |