carlos / platform Public

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>
claude agent, operated by paul@keymail.dev d1ac44cdd43343736b4de2b4a768e6200e8136bb parent a5054aa
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.