woodstar runbook: D2.5/D2.6 done — routes live, *.woodstar.app issued via delegation; D2.4 script staged
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 file changed,
+56
−24
docs/superpowers/plans/2026-08-16-woodstar-cutover-runbook.md+56 −24
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 c4dc67498..d7d80f69f 100644 |
| --- a/docs/superpowers/plans/2026-08-16-woodstar-cutover-runbook.md |
| +++ b/docs/superpowers/plans/2026-08-16-woodstar-cutover-runbook.md |
| @@ -168,12 +168,33 @@ Original checklist (for a re-run; all over |
| - [x] **D2.3 — build + ship:** DONE 2026-08-17 — six binaries + the |
| repo's `status/` site, all at `1aa534e`, shipped and promoted to |
| `canary/woodstar`. |
| -- [ ] **D2.4 — box-side (SSM, root):** system user `woodstar` |
| - (sysusers), `/data/woodstar` 0700, `/etc/carlos/apps/woodstar.env` |
| - (0600, the AASA line), six hardened units |
| - `woodstar-{relay,media,indexer,notify,unfurl,apbridge}.service` |
| - patterned on `keymail-relay.service` + `scripts/harden-host.sh` |
| - drop-ins. ExecStart flags = D1.1 verbatim, transposed: paths to |
| +- [ ] **D2.4 — box-side (SSM, root): SCRIPT STAGED, needs Paul's run** |
| + (classifier blocks remote mutations for the session — the D0.1 |
| + pattern). The complete idempotent script is at |
| + `s3://carlos-flagship-271376211898/ops/woodstar/d24-setup.sh`: |
| + user + `/data/woodstar` 0700 + env file + six hardened units |
| + (keymail-relay pattern), installed but NOT enabled/started, with |
| + **ExecStartPre founder-trap guards** — relay refuses to start |
| + without `relay.db`, indexer without `indexer.db`, apbridge without |
| + `apbridge.seed` + `apbridge.db` — so no adoption restart or stray |
| + start can ever boot an empty registration-open relay. ExecStart |
| + binaries are the ADOPTED paths |
| + (`/opt/carlos/live/woodstar-<svc>.bdm.oncarlos.com` — symlinks |
| + already on the box, placed by adoption at `1aa534e`). Run: |
| + |
| + ```bash |
| + aws --profile keymail ssm send-command --instance-ids i-092c0c1eea75723cb \ |
| + --document-name AWS-RunShellScript --parameters "$(jq -n '{commands: [ |
| + "aws s3 cp s3://carlos-flagship-271376211898/ops/woodstar/d24-setup.sh /root/d24-setup.sh && bash /root/d24-setup.sh" |
| + ]}')" |
| + ``` |
| + |
| + Unit stamping (`carlos route … -unit woodstar-<svc>.service`, run as |
| + carlos on the box, all six routes) moves to **D4.2** with the start |
| + sequence — stamping is safe earlier thanks to the guards, but it |
| + buys nothing before restore. Original spec text for reference: |
| + |
| + ExecStart flags = D1.1 verbatim, transposed: paths to |
| `/data/woodstar`, ports to the 845x map, PLUS relay fan-out flags: |
| ``` |
| @@ -190,24 +211,35 @@ Original checklist (for a re-run; all over |
| whatever Bridgy-era flags D1.1 captured, verbatim. Units installed |
| but **NOT started** (founder trap) — except as D3 rehearses on |
| copies. |
| -- [ ] **D2.5 — routes via product surface:** relay's primary route as |
| - a declared route record (#137 vocabulary) on the flagship; |
| - `status.woodstar.app` attach for `woodstar-status` (first-route |
| - static attach or alias — whichever the live #82 door supports for |
| - the shape). **`status.woodstar.app`'s exact-host route MUST exist |
| - before the wildcard alias serves** — with the alias live and the |
| - status route missing, status 302s into the SPA and the status page |
| - silently becomes the app. Then attach `woodstar.app` as alias to the |
| - relay's route. |
| -- [ ] **D2.6 — wildcard, pre-flip cert proof:** `carlos domains attach |
| - woodstar.app -wildcard -catchall` (door refuses until D0.1 is done — |
| - that refusal text is itself a check). Watch the edge obtain |
| - `*.woodstar.app` via the delegation CNAME; then handshake-verify |
| - against the flagship IP without DNS: |
| - `curl -sv --resolve paul.woodstar.app:443:99.81.104.219 |
| - https://paul.woodstar.app/ -o /dev/null 2>&1 | grep -E |
| - "subject:|expire"` → subject `CN=*.woodstar.app`. Issuance does not |
| - depend on the A records: this is the whole point of §4. |
| +- [x] **D2.5 — routes via product surface: DONE 2026-08-17**, and the |
| + shape turned out fully member-facing — no bucket hand-writes at all: |
| + - Six `kind:service` records via `carlos instances enable` (domain |
| + pinned `bdm.oncarlos.com`, channel `canary/woodstar`) + |
| + `carlos instances create -host woodstar-<svc>.bdm.oncarlos.com |
| + -addr 127.0.0.1:845x`. Routes materialized in seconds; edge |
| + already terminates TLS for them (502 until units run); **adoption |
| + delivered all six binaries to |
| + `/opt/carlos/live/woodstar-<svc>.bdm.oncarlos.com` at `1aa534e` |
| + with zero hand-copying.** |
| + - `status.woodstar.app`: first-route static attach via the door |
| + (API POST `{"host":…,"kind":"static","channel":"canary/woodstar"}` |
| + — CLI attach has no `-kind` by design). Materialized AND |
| + **already Serving** the shipped status site. Precedence invariant |
| + (exact-host route before any wildcard alias) satisfied. |
| + - `woodstar.app` attached as alias → |
| + `woodstar-relay.bdm.oncarlos.com`; DNS honestly `elsewhere` |
| + (points at old box until D4). |
| +- [x] **D2.6 — wildcard, pre-flip cert proof: DONE 2026-08-17.** |
| + Capabilities enabled through the attach door |
| + (`wildcard:true, catchall:true`); CNAME gate passed; list shows |
| + `WILDCARD yes / CATCHALL yes / DELEGATION delegated`; edge published |
| + the TXT at `_acme-challenge.woodstar.app.acme.oncarlos.com` and |
| + **`*.woodstar.app` ISSUED ~30 s after enable** — handshake-verified |
| + against 99.81.104.219: subject `CN=*.woodstar.app`, expires |
| + 2026-11-15. Per-host certs for `woodstar.app`/`status.woodstar.app` |
| + correctly wait for post-flip HTTP-01 — do NOT TLS-probe those two |
| + hosts via `--resolve` pre-flip (each probe burns a doomed ACME |
| + order against the box-wide budget). |
| ## D3 — Rehearsal (copies only, tear down after) |