carlos / platform Public

woodstar runbook: D4 EXECUTED — woodstar.app live on the flagship; three catches recorded

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 7a4356de0d5ca7290c3770e1d979aa5341b4d936 parent 0ad4e9f
1 file changed, +53 −4
  • docs/superpowers/plans/2026-08-16-woodstar-cutover-runbook.md +53 −4
diff --git a/docs/superpowers/plans/2026-08-16-woodstar-cutover-runbook.md b/docs/superpowers/plans/2026-08-16-woodstar-cutover-runbook.md
index 63681bbb4..cfb4fa573 100644
--- a/docs/superpowers/plans/2026-08-16-woodstar-cutover-runbook.md
+++ b/docs/superpowers/plans/2026-08-16-woodstar-cutover-runbook.md
@@ -299,21 +299,70 @@ dominates: final media pass + 30 s WAL wait).
## D4 — Cutover (**HELD: starts only on Paul's explicit go**; quiet hour; TTLs already 300)
-- [ ] **D4.1** old box (SSH): stop the six `woodstar-*` units → one
+**EXECUTED 2026-08-17 evening on Paul's "Go ahead with the full
+transition." Timeline and catches:**
+
+- **D4.1 freeze 18:26:15–18:26:52Z** (37 s): six units + media timer
+ stopped, final media pass, 35 s WAL wait, `litestream.service`
+ stopped. Losslessness proven by mtimes: every on-disk WAL write
+ (relay/indexer 10:36:25, apbridge 18:13:05) matched its uploaded
+ segment to the second; the 18:26:52 `.db` mtimes are litestream's
+ shutdown checkpoint of those same frames.
+- **CATCH 1 — old-bucket read denied:** `carlos-flagship-host` has no
+ policy on `woodstar-backups-271376211898` (the spec's "host role
+ already has RW" held only for carlos-data-ie). First restore attempt
+ aborted cleanly at step one. Fix: session-side
+ `aws s3 sync` of the frozen `prod/` into
+ `carlos-data-ie…/woodstar-cutover/` (box-readable), restore
+ repointed. No IAM change.
+- **D4.2 restore run: 10 s, integrity `ok` × 3**, five units up —
+ relay failed: its `EnvironmentFile=/etc/carlos/apps/woodstar.env`
+ had been SWEPT (see catch 2). Unit stamps landed for all six routes
+ (`try-restart` wiring confirmed by the verb's own output).
+- **CATCH 2 — env-file sweep:** the route-scoped config-delivery pass
+ (PR #206) owns `/etc/carlos/apps` and removed the hand-written
+ `woodstar.env` (dir mtime 18:04). `carlos env set -app
+ woodstar-relay WOODSTAR_APPLE_APP_ID=…` was set through the door
+ (kept as the durable record) but did NOT render a file for this
+ route shape — **follow-up gap to file**: config delivery for
+ kind:service unit-stamped routes. Interim fix = unit-level
+ `Environment=` line, which is exactly what the old box ran.
+- **CATCH 3 — root-owned WAL sidecars:** with the relay down through
+ catches 1–2, the litestream restart (root) opened `relay.db` first
+ and minted root-owned `-wal`/`-shm`; the woodstar user then got
+ SQLITE_CANTOPEN (14). Fix: re-chown `relay.db*` + restart. **Rule
+ for any re-run: start ALL app units and see them active BEFORE
+ restarting carlos-litestream.**
+- **D4.3 smoke:** loopback info 200 + AASA + DID; edge smoke via
+ `woodstar-relay.bdm.oncarlos.com` (feed 200) and the wildcard-cert
+ catch-all (`paul.woodstar.app --resolve` → 302 + DID) — never a TLS
+ probe of the two per-host names pre-flip.
+- **D4.4 flip 21:05Z:** both A records UPSERTed → 99.81.104.219;
+ INSYNC in ~10 s; per-host certs minted on first hit
+ (`CN=woodstar.app` exp 2026-11-15). **Full public battery green:**
+ feed/AASA/media/search 200, webfinger `activity+json`, WS 101,
+ DID `did:plc:f53efijrjuwujt7ppnp5gzf3` identical, www 301
+ URI-preserved, status page serving.
+- Old box: units stopped, disk intact, frozen 30 days (spec §7
+ rollback posture). Rollback script staged at
+ `…/ops/woodstar/d4-rollback.sh` (flagship half) + two A records
+ back — unused.
+
+- [x] **D4.1** old box (SSH): stop the six `woodstar-*` units → one
final media-backup pass → wait ~30 s for litestream's final WAL
frames → stop litestream → copy `apbridge.seed` off-box → verify
newest backup timestamps are post-stop.
-- [ ] **D4.2** flagship: litestream restore the three DBs from the OLD
+- [x] **D4.2** flagship: litestream restore the three DBs from the OLD
bucket's live prefixes + media sync + seed file into
`/data/woodstar`; `chown -R woodstar`; `PRAGMA integrity_check`;
start the six units; enable flagship litestream config + media timer
against the **fresh `carlos-data-ie` prefixes** (order: stop-old →
restore-new → start-new; the two stacks never share prefixes).
-- [ ] **D4.3** smoke BEFORE DNS: loopback `/info` 200; AASA correct at
+- [x] **D4.3** smoke BEFORE DNS: loopback `/info` 200; AASA correct at
`/.well-known/apple-app-site-association`; webfinger answers from
the bridge; `curl --resolve woodstar.app:443:99.81.104.219` through
the edge.
-- [ ] **D4.4** flip Route53 zone `Z07702842ZC4MUKZR07JB`:
+- [x] **D4.4** flip Route53 zone `Z07702842ZC4MUKZR07JB`:
`woodstar.app` A and `*.woodstar.app` A → `99.81.104.219`. Watch
per-host certs for the two named hosts mint on first post-flip hit.
- [ ] **D4.5 — Paul's browser pass:** passkey sign-in, post, image