docs: cohort-1 runbook: fence writer applied, alias plan stale, box-record proposal, #302
- Step 3: fence-writer role and box-records bucket policy applied (5 added) and merged as platform-infrastructure#46 (beb346b); serving unaffected on sophie and leo, no AccessDenied. - 7a.1: the saved alias plan was refused as stale (the host_buckets apply moved the state after it was saved); not applied, #45 unmerged; only targeted dev plans until #45 merges. - Blocker 4: no supported way to publish a box record without region HA; proposal for `carlos system box publish-record` with its cost. - Links amadan #302 for the mutation classifier versus bucket policy conflict.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+15
−4
docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md+15 −4
CI cancelled — superseded by a newer push — run details
CI log
=== gate === cd launchctx && go vet ./... && go test ./... -count=1 ok amadan.net/carlos/platform.git/launchctx 0.011s ? amadan.net/carlos/platform.git/launchctx/controlname [no test files] ? amadan.net/carlos/platform.git/launchctx/wireid [no test files] go vet ./... gofmt -l .: clean go test ./... -count=1 -timeout 30m amadan: job cancelled — a newer push superseded it
diff --git a/docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md b/docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md| index 2b7cada44..7503c2388 100644 |
| --- a/docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md |
| +++ b/docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md |
| @@ -193,8 +193,16 @@ Pass: all twelve are root-owned regular files in `/etc/systemd/system`; the edge |
| 1. **Release.** Resolved: v0.28.0, cut from 26065378 (Plan 14 Parts 2 and 3, the activation tools, e8e961dd), is fully rolled on dev (2026-10-18). Step 1. |
| 2. **Empty app segment.** 267 objects under `apps/bab//` (P5). Source: leo's app-less `proof*` routes, removed (2026-10-16). Cleanup waits for the e8e961dd release to reach leo and for Paul's approval. Step 2. |
| -3. **IAM.** Leo's object-version actions were applied on 2026-10-18 (targeted, `0 to add, 1 to change, 0 to destroy`): the claim's data store lists, reads and deletes object versions in `carlos-dev-ie-640012953448` on the seed and cold-wake paths. The fence-writer role and the deployment bucket policy are planned in carlosframework/platform-infrastructure#46 (`5 to add, 0 to change, 0 to destroy`), not applied. Step 3. |
| -4. **Box identity records.** Step 5's enrollment needs each box's signed AWS identity record at `control/boxes/<box>.json`. None exists, and none has ever been written: the publisher (`internal/host/boxrecord.go`, `boxRecordLoop`) runs only with `CARLOS_REGION_HA` on, which is off on every dev box. Leo's and sophie's records must be published before step 5, by turning region HA on for them or by a reviewed one-off publish. Open question 4. |
| +3. **IAM.** Leo's object-version actions were applied on 2026-10-18 (targeted, `0 to add, 1 to change, 0 to destroy`): the claim's data store lists, reads and deletes object versions in `carlos-dev-ie-640012953448` on the seed and cold-wake paths. The fence-writer role (`carlos-dev-ie-b-fence-writer`, bootstrap user `carlos-dev-ie-b-fence-writer-root`, no access key yet) and the deployment bucket policy were applied on 2026-10-18 from the saved targeted plan (`5 added, 0 changed, 0 destroyed`) and merged as platform-infrastructure#46 (`beb346b`). The bucket policy names that role as the only fence writer. After the apply, wakecheck still served on sophie (200) and leo answered `waking elsewhere` (sophie held the lease), with no AccessDenied in either box's journals. Step 3. |
| +4. **Box identity records.** Step 5's enrollment needs each box's signed AWS identity record at `control/boxes/<box>.json`. None exists, and none has ever been written: the publisher (`internal/host/boxrecord.go`, `boxRecordLoop`) runs only with `CARLOS_REGION_HA` on, which is off on every dev box. Leo's and sophie's records must be published before step 5. Paul ruled a reviewed one-off publish, with region HA staying off. |
| + |
| + There is no supported way today. `carlos system box` only sets roles (`system_roles.go`), and the only caller of `publishBoxRecord` (`internal/host/boxrecord.go:121`) is the agent's `boxRecordLoop`, which returns at once unless `CARLOS_REGION_HA` is on. Records are never hand-written. |
| + |
| + Proposal, the smallest platform change: a verb `carlos system box publish-record` that calls the existing `publishBoxRecord` once with the box's own label (`metricsBoxLabel`) and `readIdentityRecord`, without the region HA gate, and refuses on fleet boxes as the loop does (`shouldSkipPublisherElection`). It writes `control/boxes/<box>.json` create-only through the box's own credentials. The record is `{v: 1, box, identity_document, identity_signature, written_at}`: the instance identity document from IMDS (base64) and AWS's RSA-SHA256 signature over it, checked later against the embedded regional AWS certificates. It is AWS-signed, not signed by the box or the operator, and grants nothing until enrollment reads it. An existing record for the same instance is a no-op; one for another instance is refused. Cost: |
| + - about 40 lines plus tests (parse; no-op on the same instance; refusal on a different instance; refusal on a fleet box; no region HA needed), reusing `publishBoxRecord`'s existing tests; |
| + - copy: the usage line, the published and already-in-place lines, the refusals, and a `docs/site/cli.md` paragraph, for Paul's copy review; |
| + - census: a new root-path unit for the Plan 14 root-read overlay, and a new caller at the existing `boxid.Publish` key-site for the Plan 9 B8 record-write census; |
| + - a release and a dev roll before step 5, then one run on leo and one on sophie, each with Paul's go. |
| ## Accepted trial gaps |
| @@ -615,7 +623,9 @@ Dev's infrastructure is OpenTofu in `platform-infrastructure/dev`, so the key is |
| Status on 2026-10-18 (Paul's go to apply): |
| - The first apply created `aws_iam_role_policy.cohort1_fence_read` and the key `ef26a257-fc1d-4fe3-ad9e-77a4b7b789f6`, then failed reading the key back: the provider's refresh calls `DescribeKey`, `GetKeyPolicy`, `GetKeyRotationStatus` and `ListResourceTags` (CloudTrail), and the operator statement lacked `kms:GetKeyRotationStatus`. The key was left tainted and the alias was not created. |
| - Fix forward, approved: `kms:GetKeyRotationStatus` added to the operator statement (the file below); the live key policy replaced with that exact rendering by `aws kms put-key-policy` without the lockout bypass (equal to the file apart from AWS storing the one-element `Principal` list as a string; the operator keeps `ScheduleKeyDeletion`, `CancelKeyDeletion` and `PutKeyPolicy`); `tofu untaint aws_kms_key.cohort1_fence`. |
| -- A fresh targeted plan shows `Plan: 1 to add, 0 to change, 0 to destroy.` (the alias only). Not applied; it waits for Paul's go. |
| +- A fresh targeted plan showed `Plan: 1 to add, 0 to change, 0 to destroy.` (the alias only). On Paul's go to apply it, tofu refused it as stale (`Saved plan is stale`): the `host_buckets` apply in step 3 moved the state after the alias plan was saved. Not applied. The alias needs a new targeted plan and Paul's go; #45 stays unmerged until then. |
| +- Live today: the key (Enabled, policy as below) and leo's `cohort1-fence-read` grant. Not yet: the alias. |
| +- Until #45 merges, main's code lacks `cohort1-fence-kms.tf` while the key and grant are in state, so a whole-root plan from main would destroy them. Run only targeted plans on dev until then. |
| - The alias refresh uses `ListAliases`, an account-level action covered by the operator's IAM policy, not the key policy. |
| Its content: |
| @@ -1392,4 +1402,5 @@ For platform-1e: |
| 1. Should `hostops.Apps` skip an empty app segment instead of refusing, so one stray object cannot stop activation on a whole deployment? |
| 2. For a later teardown out of hold: how do writes made under the claim reach the claims-off restore path? |
| 3. Answered 2026-10-18: leo's object-version actions are applied for the trial; the box-records bucket policy goes with the fence-writer role in platform-infrastructure#46. |
| -4. How should leo's and sophie's box identity records (`control/boxes/<box>.json`) be published for step 5, given `CARLOS_REGION_HA` is off on both? |
| +4. Box identity records: Paul ruled a reviewed one-off publish with region HA off. No supported verb exists; the proposal in blocker 4 needs a decision, a build, copy review, census rows, a release and a dev roll. |
| +5. The console's mutation classifier lets it delete under `control/boxes/` and `control/box-volumes/`, which the new box-records bucket policy refuses: amadan carlos/platform#302. |