docs: cohort-1 runbook: 7a.2 PASS (fence-writer credentials on leo, parameter deleted, CloudTrail clean); KMS teardown planned, apply awaits go
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+47
−0
docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md+47 −0
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 873930e83..28fcba17f 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 |
| @@ -1937,6 +1937,53 @@ Afterwards, record in the trial notes that the CloudTrail events for `PutParamet |
| On failure: hold, but always finish parts 3 to 5 first, so no readable copy of the credentials is left in SSM and no decrypt grant stays open. |
| +### 7a.2 record (2026-10-19) |
| + |
| +Run by the executor for the controller, on Paul's go confirmed directly with the controller, with the steps as platform-1e specified them. Commands went from DOCS-T1 (profile `kass`, `user/remote-agent`), one SSM `send-command` per leo block. No credential value appeared in a command line, terminal output, an SSM parameter or output, a log, git or a kept file. This record holds only the access key ID's last 4 characters and SHA-256 prefixes. |
| + |
| +**7a.2 PASS for parts 1 to 4: credentials installed on leo, parameter deleted after about 18 seconds, CloudTrail clean. Part 5 (the KMS teardown) is planned, not applied: it waits for Paul's go.** |
| + |
| +Where this run differed from the blocks above: |
| +- There was no `fence-credentials.json` from step 3 (the bootstrap user had 0 access keys). The key was created in this sitting: `iam create-access-key` wrote straight to a `0600` file in a private `0700` scratch directory under `$TMPDIR` on DOCS-T1. A Python step built the source file from it (`O_EXCL`, `0600`, compact JSON, 218 bytes) in the exact format `decodeFenceCredentials` takes at `f3bf1214` (`internal/hostfence/writer.go`). That format is five fields, `role`, `region`, `bucket`, `access_key` and `secret_key`, and the decoder refuses unknown fields. Values: role `arn:aws:iam::640012953448:role/carlos-dev-ie-b-fence-writer`, region `eu-west-1` (the bucket's location), bucket `carlos-dev-640012953448`. |
| +- The fence role ARN was written into the leo block instead of staged with `box_put`. It is an ARN, not a secret. |
| +- The leo block ran inside a shell function fed `</dev/null`, so no command in it could read the rest of the script from bash's stdin. Besides the runbook's fingerprint, it printed a presence flag and `secret_sha8`, the first 8 hex digits of the SHA-256 of `secret_key`. |
| +- Part 5 stopped at the plan. The parameter is gone, so nothing is left to decrypt, but the key, the alias and leo's grant stay live until the apply. |
| + |
| +Pre-checks, read-only, 20:18Z to 20:20Z, all PASS: |
| +- The roster `control/host-actors.json` is 2227 bytes, etag `a69f6d93…`, revision 9, `published`, home `ie`. `leo-climbing` is `active` with its census key bound. Leo's report (20:17:34Z) shows `floor_guard_ready` `True`, floor `1a6a7e50`, binary `736826e4`, `v0.28.4`. |
| +- On leo (20:19:16Z), the guard is armed. `guard` and `guard/identities` are `755 root:root`, `sudo -u carlos test -x guard/carlos` passes, and `policy.json` and `identities/v0.28.4.json` are present. `carlos v0.28.4`. Edge and instance-control are `active` since 07:51:53Z. `CARLOS_HOST_CLAIM` is absent from `host.env` (root `600`). Neither `/etc/carlos/fence-writer` nor `/run/carlos-fence-stage` exists. `/run` is tmpfs. `/usr/bin/aws` is aws-cli 2.23.6. |
| +- Key `ef26a257-fc1d-4fe3-ad9e-77a4b7b789f6` is `Enabled`, and `alias/cohort1-fence-leo` points at it. The key policy SIDs are as in 7a.1. |
| +- `carlos-dev-host-b` carries `cohort1-fence-read` (`ssm:GetParameter` on the one parameter ARN). |
| +- `describe-parameters` found no `/carlos/cohort1/fence/carlos-dev-ie-b`. |
| +- `carlos-dev-ie-b-fence-writer-root` had 0 access keys. Role `carlos-dev-ie-b-fence-writer` trusts only `user/carlos/fence-writers/carlos-dev-ie-b-fence-writer-root`. |
| +- Simulated for `remote-agent`: `iam:CreateAccessKey`, `DeleteAccessKey` and `ListAccessKeys` on the user, and `ssm:PutParameter` and `DeleteParameter` on the parameter, are all `allowed`. Leo's role is `allowed` `ssm:GetParameter` on it. |
| +- Noted, not a stop: leo has a swap device (`swapon --show`). The staging file lived about a second on `/run` tmpfs, and the installed copy is on disk by design. |
| + |
| +Transfer, 20:20:44Z to 20:21:07Z: |
| +1. Access key created at 20:20:45Z, `Active`, ID ending `VINM`. `secret_sha8` `8e78932b`, fingerprint `3fe9f5d9`. |
| +2. `put-parameter --type SecureString --key-id alias/cohort1-fence-leo --value file://…`: version `1`, at 20:20:46Z. |
| +3. Leo, 20:20:52Z, command `e2e93233-81e7-4751-93bb-8560cc3ba839`: `staged present=yes`, `root 600 1 219` (the 218 bytes plus the CLI's newline), fingerprint `3fe9f5d9` and `secret_sha8` `8e78932b`, both equal to DOCS-T1's. `fence-credentials --dry-run`, then the apply, each printed the summary with `"target": "/etc/carlos/fence-writer/credentials.json"`, `"mode": "0600"`, bucket `carlos-dev-640012953448`, `aws_region` `eu-west-1` and the role above. Then `subshell exit=0`, `staging file gone`, and the stage directory was removed (`stat`: no such file). `/etc/carlos/fence-writer` is `root:root 700`, and `credentials.json` and `credentials.installation.json` are both `root:root 600 1`. `/etc/carlos` and the guard directories are still `755 root:root`. |
| +4. `delete-parameter` at 20:21:05Z. `describe-parameters` returned empty, and `get-parameter` returned `ParameterNotFound`. The parameter existed from 20:20:46Z to 20:21:05Z. |
| +5. On DOCS-T1, `shred -u` ran on both files (the `create-access-key` response and the source file) and the secret directory was removed, at 20:21:07Z. |
| + |
| +Leo afterwards, 20:21:42Z, read-only: |
| +- The installed file's fingerprint is `3fe9f5d9` and its `secret_sha8` is `8e78932b`. The installation receipt (`v` 1) equals the file's sha256 (`c34da580`). |
| +- `fence-credentials --dry-run` with the installed file as `--source` passed (`dry_run: true`, exit 0). It passes only when the installed file and the receipt match. |
| +- Edge and instance-control are `active`, `NRestarts=0`, and unrestarted since 07:51:53Z. `CARLOS_HOST_CLAIM` is still absent. `host.env` mtime is 2026-10-18 09:48:57Z, so it is unchanged. The guard is `755` and `test -x` passes. 0 AccessDenied lines since 20:15Z. |
| +- Serving at 20:21:48Z: claimtrial 200 via `--resolve` to leo (`18.200.150.210`, `host=ip-172-31-19-180`), and wakecheck 200. |
| + |
| +CloudTrail since 20:20:00Z, read at 20:24Z in eu-west-1 and us-east-1. No credential value in any request or response: |
| +- `PutParameter` (`remote-agent`, 20:20:46Z): `value` is `HIDDEN_DUE_TO_SECURITY_REASONS`. |
| +- `kms:Encrypt` (`remote-agent`, 20:20:46Z): the request holds only the algorithm, `keyId` and the `PARAMETER_ARN` context. |
| +- `GetParameter` `withDecryption` (20:20:53Z) and `kms:Decrypt` (20:20:53Z), both from `assumed-role/carlos-dev-host-b/i-046c7fe43620944bc`, with the trial parameter's context. That was the only `kms:Decrypt` caller. |
| +- `CreateAccessKey` (us-east-1, 20:20:45Z): `responseElements.accessKey` holds only `accessKeyId`, `userName`, `status` and `createDate`. No `secretAccessKey` field is logged. |
| +- The two `SendCommand`s carry the base64 script only. `DeleteParameter`, `DescribeParameters` and `GetParameter` (`ParameterNotFound`) carry the name only. |
| +- A scan of every matching event's request and response for 40-character key-like strings found 0. |
| + |
| +Part 5, the KMS teardown, planned and not applied. On DOCS-T1 there is a fresh worktree of the platform-infrastructure GitHub clone, `platform-infrastructure-cohort1-kms-remove`, on branch `claim-cohort1-fence-kms-remove`, made from main `a0b3f5a`. `dev/cohort1-fence-kms.tf` is removed in commit `c31fd38`, as PR carlosframework/platform-infrastructure#48. The targeted plan (the three `-target`s above) is saved as `dev/cohort1-fence-remove.plan`, sha256 `3c8d5f67…5e9e`: `Plan: 0 to add, 0 to change, 3 to destroy.` It deletes `aws_iam_role_policy.cohort1_fence_read`, `aws_kms_alias.cohort1_fence` and `aws_kms_key.cohort1_fence` (7-day window). Nothing else changes, outputs included. After Paul's go, apply that plan file, then run part 5's two checks (`allowed`, then `PendingDeletion`). |
| + |
| +State now: leo holds the only copy of the fence-writer credentials, root `600` at `/etc/carlos/fence-writer/credentials.json`. The bootstrap user has one `Active` access key (`…VINM`). No parameter exists. The key, the alias and leo's read grant are live until the apply. Leo's host claims are still off, and no host.env or flag changed. Next: the KMS apply on Paul's go, then step 8 with its own go. |
| + |
| ## Step 8. Leo's attended offline window |
| What: Plan 9 milestone 4 on leo, with the files seed inside the same window. The commands are quoted from platform-1e's run 11 report ("Leo's attended offline window"); the quoted text in each `[...]` block is the files seed copy Paul approved on 2026-10-15, verbatim. This is an attended procedure: there is no automatic transaction and no resume after a reboot. |