docs: cohort-1 runbook: 5.0 done, operator ruled, one signing session after v0.28.2
- Step 5.0 recorded as done 2026-10-18: leo 06:10:49 (leo-climbing.json, 919 B) and sophie 06:11:32 (sophie-knitting.json, 922 B), both verify re-runs "nothing was written"; wakecheck 200, edges active, no new AccessDenied. - Operator paul@keymail.dev (Paul's ruling) in leo.json, sophie.json and the README; removed from TO-CONFIRM. - Paul's ruling: the signer is created once, after v0.28.2, for one session covering 5.1, 6 and 7. New "The signing session" section (open: create signer; 5.1; 6; 7; close) and "Closing the signing session" (shred, terminate, delete role/profile/SG, CloudTrail check). Nothing else touches the signer between. Stop-and-hold, blockers and signing-host.md updated; the trial waits for v0.28.2 past 5.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 files changed,
+95
−56
docs/superpowers/notes/claim-trial-inputs/README.md+11 −10docs/superpowers/notes/claim-trial-inputs/leo.json+1 −1docs/superpowers/notes/claim-trial-inputs/signing-host.md+5 −5docs/superpowers/notes/claim-trial-inputs/sophie.json+1 −1docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md+77 −39
CI passed — run details
CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok amadan.net/carlos/platform.git/launchctx 0.012s
? 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
ok github.com/carlosframework/platform 321.033s
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 3.030s
ok github.com/carlosframework/platform/cmd/moveprobe 92.203s
ok github.com/carlosframework/platform/cmd/zddprobe 0.702s
ok github.com/carlosframework/platform/internal/activator 251.561s
ok github.com/carlosframework/platform/internal/agentsock 0.016s
ok github.com/carlosframework/platform/internal/alerting 0.003s
ok github.com/carlosframework/platform/internal/analytics 0.022s
ok github.com/carlosframework/platform/internal/appconfig 2.515s
ok github.com/carlosframework/platform/internal/apperr 0.071s
ok github.com/carlosframework/platform/internal/applog 0.126s
ok github.com/carlosframework/platform/internal/awsinv 0.003s
ok github.com/carlosframework/platform/internal/boxchoice 0.004s
ok github.com/carlosframework/platform/internal/boxid 0.644s
? github.com/carlosframework/platform/internal/boxid/boxidtest [no test files]
? github.com/carlosframework/platform/internal/boxid/internal/signtest [no test files]
ok github.com/carlosframework/platform/internal/boxvolumes 0.008s
ok github.com/carlosframework/platform/internal/caps 0.013s
ok github.com/carlosframework/platform/internal/certwarm 0.002s
ok github.com/carlosframework/platform/internal/cgroup 0.696s
ok github.com/carlosframework/platform/internal/changelog 0.312s
ok github.com/carlosframework/platform/internal/claimactivation 0.023s
ok github.com/carlosframework/platform/internal/clamd 0.054s
? github.com/carlosframework/platform/internal/cliflag [no test files]
ok github.com/carlosframework/platform/internal/console 411.062s
? github.com/carlosframework/platform/internal/controlname [no test files]
? github.com/carlosframework/platform/internal/controlsock [no test files]
ok github.com/carlosframework/platform/internal/dbdoor 6.043s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.008s
ok github.com/carlosframework/platform/internal/dbverify 0.004s
ok github.com/carlosframework/platform/internal/dnsrec 0.004s
ok github.com/carlosframework/platform/internal/docsite 0.005s
ok github.com/carlosframework/platform/internal/domains 2.416s
ok github.com/carlosframework/platform/internal/doorkey 0.006s
ok github.com/carlosframework/platform/internal/economics 0.004s
ok github.com/carlosframework/platform/internal/edge 44.978s
? github.com/carlosframework/platform/internal/edgeproof [no test files]
ok github.com/carlosframework/platform/internal/edgesfile 0.008s
ok github.com/carlosframework/platform/internal/fleetapi 0.134s
ok github.com/carlosframework/platform/internal/gate 1.661s
ok github.com/carlosframework/platform/internal/genselect 0.015s
ok github.com/carlosframework/platform/internal/geo 0.016s
ok github.com/carlosframework/platform/internal/geoip 0.031s
ok github.com/carlosframework/platform/internal/historyreadback 0.155s
ok github.com/carlosframework/platform/internal/host 175.577s
ok github.com/carlosframework/platform/internal/hostactors 0.489s
ok github.com/carlosframework/platform/internal/hostbox 2.675s
ok github.com/carlosframework/platform/internal/hostclaim 0.569s
ok github.com/carlosframework/platform/internal/hostevidence 0.014s
ok github.com/carlosframework/platform/internal/hostexec 4.812s
ok github.com/carlosframework/platform/internal/hostfence 0.029s
ok github.com/carlosframework/platform/internal/hostfiles 10.413s
ok github.com/carlosframework/platform/internal/hostfloor 0.014s
ok github.com/carlosframework/platform/internal/hostguard 1.031s
ok github.com/carlosframework/platform/internal/hostops 1.682s
? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files]
ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.014s
ok github.com/carlosframework/platform/internal/hostquarantine 0.014s
ok github.com/carlosframework/platform/internal/hostrelocate 0.437s
ok github.com/carlosframework/platform/internal/hostresolve 0.155s
ok github.com/carlosframework/platform/internal/hostwrite 0.014s
ok github.com/carlosframework/platform/internal/infraview 0.015s
ok github.com/carlosframework/platform/internal/instancectl 5.218s
ok github.com/carlosframework/platform/internal/instanceexec 2.760s
ok github.com/carlosframework/platform/internal/instancefs 0.018s
ok github.com/carlosframework/platform/internal/instancekey 0.016s
ok github.com/carlosframework/platform/internal/instanceupdates 0.008s
ok github.com/carlosframework/platform/internal/layout 1.539s
ok github.com/carlosframework/platform/internal/lbhealth 0.002s
ok github.com/carlosframework/platform/internal/lease 9.807s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.007s
ok github.com/carlosframework/platform/internal/ledger 0.011s
ok github.com/carlosframework/platform/internal/mail 0.172s
ok github.com/carlosframework/platform/internal/maintenance 0.086s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.481s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.072s
ok github.com/carlosframework/platform/internal/metrics 0.004s
ok github.com/carlosframework/platform/internal/moves 21.080s
ok github.com/carlosframework/platform/internal/moves/archive 0.805s
ok github.com/carlosframework/platform/internal/objstore 0.189s
ok github.com/carlosframework/platform/internal/ops 0.207s
ok github.com/carlosframework/platform/internal/park 0.200s
ok github.com/carlosframework/platform/internal/privatefile 0.005s
ok github.com/carlosframework/platform/internal/provision 18.024s
ok github.com/carlosframework/platform/internal/publicrelease 0.006s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 5.461s
ok github.com/carlosframework/platform/internal/relay 7.232s
ok github.com/carlosframework/platform/internal/release 21.688s
ok github.com/carlosframework/platform/internal/rollout 3.640s
ok github.com/carlosframework/platform/internal/rolloutwords 0.003s
ok github.com/carlosframework/platform/internal/rootaws 0.006s
ok github.com/carlosframework/platform/internal/rootparticipation 0.058s
ok github.com/carlosframework/platform/internal/rootstate 0.026s
ok github.com/carlosframework/platform/internal/runs 0.583s
ok github.com/carlosframework/platform/internal/s3compat 0.005s
ok github.com/carlosframework/platform/internal/safefile 0.004s
ok github.com/carlosframework/platform/internal/schedule 2.502s
ok github.com/carlosframework/platform/internal/seeds 0.011s
ok github.com/carlosframework/platform/internal/sim/hostlife 8.909s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 3.560s
ok github.com/carlosframework/platform/internal/status 0.041s
ok github.com/carlosframework/platform/internal/steering 0.006s
ok github.com/carlosframework/platform/internal/stopevidence 0.249s
ok github.com/carlosframework/platform/internal/svcrole 0.002s
ok github.com/carlosframework/platform/internal/telemetry 0.004s
ok github.com/carlosframework/platform/internal/termfence 0.004s
ok github.com/carlosframework/platform/internal/tunnelclient 0.004s
ok github.com/carlosframework/platform/internal/unixdial 0.002s
ok github.com/carlosframework/platform/internal/updatewords 0.004s
ok github.com/carlosframework/platform/internal/usage 0.004s
ok github.com/carlosframework/platform/internal/version 0.001s
ok github.com/carlosframework/platform/internal/vet 1.562s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
--- shard 1 of 5 ---
cross-compiling darwin/amd64
--- shard 2 of 5 ---
cross-compiling linux/arm64
linux/arm64 carlos binary: 48824482 bytes (ceiling 52000000)
--- shard 3 of 5 ---
cross-compiling windows/amd64
--- shard 4 of 5 ---
cross-compiling windows/arm64
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
minio: cached, digest verified
mc: cached, digest verified
minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
buckets and scoped user ready
=== RUN TestDeliveredContractMinio
=== RUN TestDeliveredContractMinio/put
=== RUN TestDeliveredContractMinio/get_round-trips_the_body
=== RUN TestDeliveredContractMinio/list_shows_the_key
=== RUN TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN TestDeliveredContractMinio/delete_removes_it
=== RUN TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.02s)
--- PASS: TestDeliveredContractMinio/put (0.00s)
--- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
--- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
--- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.01s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
--- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok github.com/carlosframework/platform/internal/objstore 0.023s
=== RUN TestT4StorageProbeMinio
2026/10/18 06:26:38 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DF8BB7D17595DB, HostID: 7c420053e0fbcd7f41653fdd92ffa2802c903d05303b1aa15adc2e8bb65d183f, api error SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
--- PASS: TestT4StorageProbeMinio (0.01s)
PASS
ok github.com/carlosframework/platform/internal/host 0.021s
minio contract: PASS
diff --git a/docs/superpowers/notes/claim-trial-inputs/README.md b/docs/superpowers/notes/claim-trial-inputs/README.md| index b043f5279..13cac6b6d 100644 |
| --- a/docs/superpowers/notes/claim-trial-inputs/README.md |
| +++ b/docs/superpowers/notes/claim-trial-inputs/README.md |
| @@ -2,7 +2,7 @@ |
| These are the concrete inputs the runbook's steps 5 to 7 say "the controller supplies and Paul approves" (`docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md`). They were drafted on 2026-10-18 from dev values read with read-only calls, then updated with platform-1e's rulings the same day. Nothing here is signed, and no secret is in any file. A value still open is marked `TO-CONFIRM`; one that waits on a platform fix is marked `BLOCKED`. |
| -Timing: step 5 (roster and enrollment) needs only v0.28.1 on dev and Paul's approval of the package. Step 6 (arming) and later wait for v0.28.2, which carries the fixes for gaps 1 and 2. |
| +Timing: step 5.0 (box identity records) is done. Everything after it, from 5.1 (roster and enrollment) on, waits for v0.28.2, which carries the fixes for gaps 1 and 2, and runs in one signing session (Paul's ruling, 2026-10-18). |
| Platform main at drafting: `b1118925` (region HA 2b). The box-record verb is on branch `box-publish-record` (`6040bb1c`), merging for v0.28.1. |
| @@ -31,7 +31,7 @@ Platform main at drafting: `b1118925` (region HA 2b). The box-record verb is on |
| | Pools | `cohort1` (leo), `console` (sophie) | leo: runbook step 4 sets `CARLOS_POOLS=cohort1`. Sophie: platform-1e ruling 2026-10-18; `console` keeps sophie out of cohort1's required set, and with claims off it does not affect sophie's serving | confirmed | |
| | Box references and labels | `leo-climbing`, `sophie-knitting` | each box's name in `carlos system status` and its `metricsBoxLabel`; the record key is `control/boxes/<reference>.json` | confirmed | |
| | Instance IDs | leo `i-046c7fe43620944bc`, sophie `i-05498eada6f0afd23` | IMDS on each box | confirmed (revalidate before the run) | |
| -| Operator | suggested default `paul@keymail.dev` | the identity of the person approving enrollment, recorded in the roster | TO-CONFIRM (Paul) | |
| +| Operator | `paul@keymail.dev` | Paul's ruling 2026-10-18; the identity of the person approving enrollment, recorded in the roster | confirmed | |
| | Console ID | `dev-console` | platform-1e ruling 2026-10-18 | confirmed | |
| | Console enrollment | 32 lowercase hex characters | `hostactors` token rule `^[0-9a-f]{32}$`; chosen once at approval and kept | TO-CONFIRM (generated at approval) | |
| | Console deployment bucket | `carlos-dev-640012953448` | dev deployment bucket | confirmed | |
| @@ -63,18 +63,19 @@ platform-1e confirmed gaps 1 and 2 on main on 2026-10-18 and is building the fix |
| ## Open items |
| TO-CONFIRM: |
| -1. Operator identity for both boxes (Paul; suggested default `paul@keymail.dev`). |
| -2. Console enrollment token, 32 lowercase hex characters (generated at approval). |
| -3. v0.28.2 published: the router sha256 from its signed `SHA256SUMS.txt`, the console sha256 from the console app's signed release manifest, and the carlos v0.28.2 CLI sha256 for the signer. This also fixes the floor's `min_release`. |
| -4. The bucket keys the signer needs, confirmed by a `--dry-run` of every phase from the signer. |
| +1. Console enrollment token, 32 lowercase hex characters (generated at approval). |
| +2. v0.28.2 published: the router sha256 from its signed `SHA256SUMS.txt`, the console sha256 from the console app's signed release manifest, and the carlos v0.28.2 CLI sha256 for the signer. This also fixes the floor's `min_release`. |
| +3. The bucket keys the signer needs, confirmed by a `--dry-run` of every phase from the signer. |
| TO-CREATE (each with Paul's go): the `carlos-claim-signer` security group (its ID), role, instance profile and instance (`signing-host.md`). |
| Known now: v0.28.1 is published and on dev; the carlos v0.28.1 and jq 1.7.1 sha256 values, the AMI, the VPC and the subnet are in `signing-host.md`. |
| BLOCKED (fixes ship in v0.28.2, built by platform-1e): |
| -5. `policy.json`: the signed armed policy (gap 1). |
| -6. `identity.json`: the signed v0.28.2 system identity (gap 2). |
| -7. Sophie's protected console path and its `.prev` (gap 2; `.prev` may equal current). |
| +4. `policy.json`: the signed armed policy (gap 1). |
| +5. `identity.json`: the signed v0.28.2 system identity (gap 2). |
| +6. Sophie's protected console path and its `.prev` (gap 2; `.prev` may equal current). |
| -Confirmed on 2026-10-18: namespace `platform`, sophie's pool `console`, console ID `dev-console`. |
| +Confirmed on 2026-10-18: namespace `platform`, sophie's pool `console`, console ID `dev-console`, operator `paul@keymail.dev`. Box identity records published (step 5.0): `control/boxes/leo-climbing.json` and `control/boxes/sophie-knitting.json`. |
| + |
| +Timing (Paul's ruling, 2026-10-18): the signer is created once, after v0.28.2, for one session that covers 5.1 through step 7. |
diff --git a/docs/superpowers/notes/claim-trial-inputs/leo.json b/docs/superpowers/notes/claim-trial-inputs/leo.json| index 4661a6e60..be07a2bf7 100644 |
| --- a/docs/superpowers/notes/claim-trial-inputs/leo.json |
| +++ b/docs/superpowers/notes/claim-trial-inputs/leo.json |
| @@ -6,5 +6,5 @@ |
| "pool": "cohort1", |
| "aws_region": "eu-west-1", |
| "instance_id": "i-046c7fe43620944bc", |
| - "operator": "TO-CONFIRM (Paul): the approving operator's identity; suggested default paul@keymail.dev" |
| + "operator": "paul@keymail.dev" |
| } |
diff --git a/docs/superpowers/notes/claim-trial-inputs/signing-host.md b/docs/superpowers/notes/claim-trial-inputs/signing-host.md| index 53c831c84..ff05084d1 100644 |
| --- a/docs/superpowers/notes/claim-trial-inputs/signing-host.md |
| +++ b/docs/superpowers/notes/claim-trial-inputs/signing-host.md |
| @@ -39,7 +39,7 @@ The signer reuses the AMI, type, subnet and launch options. It gets its **own** |
| | Access | SSM Session Manager only. No SSH key, no inbound port | |
| | Metadata | IMDSv2 required, hop limit 1 | |
| | Storage | default root volume, deleted on termination. No swap (Debian cloud default), so a key in `/run` stays in memory | |
| -| Lifetime | one instance per signing session (step 5, then step 7), terminated straight after each | |
| +| Lifetime | created once, after v0.28.2 is on dev, for a single signing session that covers runbook 5.1, 6 and 7 in order (Paul's ruling, 2026-10-18), so the release key is on a host only once. Nothing else touches the signer during the session. Torn down at its end, or at any stop | |
| | Tags | `Name=carlos-claim-signer`, `purpose=claim-signer` | |
| ## IAM role `carlos-claim-signer` |
| @@ -96,10 +96,10 @@ The key goes from the SSM response straight into a `0600` file on tmpfs (`/run`) |
| Known values (2026-10-18): |
| - carlos v0.28.1 `carlos-linux-arm64`: `2ae174da29b1d10fdcb009172ba830db5460b62e838bcecc7d9e17535f35adea`. It comes from the published release's `SHA256SUMS.txt`, whose signature verified with the README's pinned key (`openssl pkeyutl -verify`: "Signature Verified Successfully"). It is the same binary leo and sophie run. |
| - jq 1.7.1 `jq-linux-arm64`: `4dd2d8a0661df0b22f1bb9a1f9830f06b6f3b8f7d91211a1ef5d7c4f06a8b4a5` (1709616 bytes). It was computed on DOCS-T1 from the file downloaded from `github.com/jqlang/jq/releases/download/jq-1.7.1/`, and matches that release's `sha256sum.txt`. |
| -- carlos v0.28.2: TO-CONFIRM when published (step 6's policy signing and step 7 need it). |
| +- carlos v0.28.2: TO-CONFIRM when published. The signing session uses it. |
| On DOCS-T1, before launching: |
| -1. Download the carlos CLI `carlos-linux-arm64` (v0.28.1 for step 5; v0.28.2 for step 6's policy signing and step 7), with its `SHA256SUMS` and `SHA256SUMS.sig`, from the public release. Verify the signature as the README's "Releasing the platform" verification does, then `sha256sum -c`. Record the sha256. |
| +1. Download the carlos CLI `carlos-linux-arm64` (v0.28.2: the session runs 5.1 through 7, and step 6's policy signing needs v0.28.2), with its `SHA256SUMS` and `SHA256SUMS.sig`, from the public release. Verify the signature as the README's "Releasing the platform" verification does, then `sha256sum -c`. Record the sha256. |
| 2. Download jq 1.7.1 `jq-linux-arm64` and the release's `sha256sum.txt` from `github.com/jqlang/jq/releases/tag/jq-1.7.1`, then check it with `sha256sum -c`. Record the sha256. |
| 3. Upload both to `s3://carlos-dev-640012953448/claim-signer/<run-id>/`. |
| @@ -114,7 +114,7 @@ chmod 0755 /usr/local/bin/carlos /usr/local/bin/jq |
| /usr/local/bin/carlos version |
| ``` |
| -Expected: both `OK`, then `carlos v0.28.1`. For step 6 onwards, use the v0.28.2 binary and its sha256 instead (TO-CONFIRM when published). |
| +Expected: both `OK`, then `carlos v0.28.2`. The session uses the v0.28.2 binary: replace the carlos sha256 above with v0.28.2's (TO-CONFIRM when published). The v0.28.1 value is kept for reference. |
| ## Create (each part needs Paul's go) |
| @@ -157,7 +157,7 @@ aws ec2 terminate-instances --instance-ids "$I" && aws ec2 wait instance-termina |
| aws s3 rm "s3://carlos-dev-640012953448/claim-signer/<run-id>/<each exact key>" |
| ``` |
| -After step 7, when no more signing is needed: |
| +At the end of the session (after step 7, or at any stop), straight after terminating the instance: |
| ```bash |
| aws iam remove-role-from-instance-profile --instance-profile-name carlos-claim-signer --role-name carlos-claim-signer |
diff --git a/docs/superpowers/notes/claim-trial-inputs/sophie.json b/docs/superpowers/notes/claim-trial-inputs/sophie.json| index 571dfe272..a08c08028 100644 |
| --- a/docs/superpowers/notes/claim-trial-inputs/sophie.json |
| +++ b/docs/superpowers/notes/claim-trial-inputs/sophie.json |
| @@ -6,5 +6,5 @@ |
| "pool": "console", |
| "aws_region": "eu-west-1", |
| "instance_id": "i-05498eada6f0afd23", |
| - "operator": "TO-CONFIRM (Paul): the approving operator's identity; suggested default paul@keymail.dev" |
| + "operator": "paul@keymail.dev" |
| } |
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 f79788fcd..2f08bc6fc 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 |
| @@ -191,13 +191,13 @@ Pass: all twelve are root-owned regular files in `/etc/systemd/system`; the edge |
| ## Blockers |
| -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. Two dev-only releases follow, each rolled to dev with Paul's go: v0.28.1 (the box-record verb), which unblocks step 5, and v0.28.2 (gap 1 and 2 fixes), which step 6 waits for. |
| +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. Two dev-only releases follow, each rolled to dev with Paul's go: v0.28.1 (the box-record verb), rolled 2026-10-18 and used by step 5.0 (done), and v0.28.2 (gap 1 and 2 fixes), which everything past 5.0 waits for. |
| 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 (`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. |
| +4. **Box identity records.** Resolved 2026-10-18 (step 5.0 done: `control/boxes/leo-climbing.json` and `control/boxes/sophie-knitting.json`). 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. |
| The verb `carlos system box publish-record` (branch `box-publish-record`, merging now) ships in v0.28.1, dev only, and is the supported way. It replaces the agent's region-HA-only publisher for this trial; step 5.0 runs it once per box. The record is AWS-signed (the EC2 instance identity document and AWS's signature over it), not signed by the box or the operator, and grants nothing until enrollment reads it. |
| -5. **Signed armed policy and system identity (step 6 onwards).** Step 6's `guard-arm` needs an armed `policy.json` (the floor plus a root-signed `moves-protocol` authorization) and a signed system identity at `control/system/router/releases/<tag>.json`. On main, nothing signs the policy without publishing it, and nothing publishes a system identity; the dev bucket has none. platform-1e confirmed both gaps on 2026-10-18 and is building the fixes, with Paul's go: (a) a `claims run` phase that signs the armed policy's authorization for a floor on the trusted host without publishing anything; (b) the release workflow publishing the signed system identity (router and console), and the protected console install with its `.prev`. Both ship in v0.28.2, so the floor's `min_release` is v0.28.2. Step 5 does not need them. Details in `docs/superpowers/notes/claim-trial-inputs/README.md`. |
| +5. **Signed armed policy and system identity (everything past 5.0).** Step 6's `guard-arm` needs an armed `policy.json` (the floor plus a root-signed `moves-protocol` authorization) and a signed system identity at `control/system/router/releases/<tag>.json`. On main, nothing signs the policy without publishing it, and nothing publishes a system identity; the dev bucket has none. platform-1e confirmed both gaps on 2026-10-18 and is building the fixes, with Paul's go: (a) a `claims run` phase that signs the armed policy's authorization for a floor on the trusted host without publishing anything; (b) the release workflow publishing the signed system identity (router and console), and the protected console install with its `.prev`. Both ship in v0.28.2, so the floor's `min_release` is v0.28.2. By Paul's ruling the signing session (5.1 to 7) opens only after v0.28.2, so this blocker gates everything past 5.0. Details in `docs/superpowers/notes/claim-trial-inputs/README.md`. |
| ## Accepted trial gaps |
| @@ -240,7 +240,7 @@ Hazards: H3 (#272, never the old road on leo) and H4 (the old-updater instance-c |
| On failure: `carlos system rollback --console https://platform.dev.carlosframework.com --channel stable` (and `canary`). This is an ordinary claims-off release rollback, and it works only while no actor is armed. From step 6 on, never run below the floor. |
| -Failure handling for the rest of the runbook: steps 1 to 4 change nothing claim-related, so their own undo applies. From step 5 on, a failure means stop and hold. Before step 8 there are no claim-mode units, so "hold" means: stop the runbook, change nothing more, and leave leo serving claims off as it is. From step 8 on, follow "Stop and hold". |
| +Failure handling for the rest of the runbook: steps 1 to 4 change nothing claim-related, so their own undo applies. From step 5.1 on, a failure means stop and hold. Before step 8 there are no claim-mode units, so "hold" means: stop the runbook, change nothing more, and leave leo serving claims off as it is. From step 8 on, follow "Stop and hold". |
| ## Step 2. Clean up the empty app segment |
| @@ -311,43 +311,18 @@ Why a pool: dev's two home boxes both mint every unregioned, pool-less record. A |
| `claimtrial` is the only host the files seed and acceptance touch. It holds nothing anyone needs. |
| -## Steps 5 to 7: the activation tools |
| +## Step 5. Box identity records, roster and enrollment |
| -Steps 5 to 7 use `carlos system claims` from main. Its help text on main: |
| +Step 5.0 is done. 5.1 (roster and enrollment) runs inside the signing session, after v0.28.2 (see "The signing session"). |
| -``` |
| -usage: carlos system claims <command> [flags] |
| - |
| - inspect --bucket B --aws-region R --public-key K |
| - run --plan FILE --signing-key FILE --public-key K --aws-region R |
| - evidence-key --roster FILE --expect-revision N --bucket B --box BOX --enrollment E --public-key K |
| - fence-credentials --source FILE --bucket B --aws-region R --role ARN |
| - console-config --roster FILE --expect-revision N --bucket B --console-id ID --public-key K [--enable-console-runtime] |
| - console-report --bucket B --aws-region R --expect-revision N --console-id ID --public-key K |
| - install-control |
| - |
| -For commands that write, add --dry-run to validate and print the planned operation without writing. |
| -Run signing phases on the trusted operator host. Run box installers as root. |
| -``` |
| - |
| -`run` takes a JSON plan whose `phase` is one of `initialize`, `configure`, `invite-box`, `enroll-box`, `invite-console`, `enroll-console`, `publish-floor`, `home` or `bind-key`, with `bucket` and `expected_revision`. Every `carlos` command below is quoted from platform-1e's report (run 2, sections A to C; run 11 confirms these command branches are byte-identical on main), and every flag was checked against main's code. |
| +### 5.0 Box identity records (done 2026-10-18) |
| -**No new tools on the boxes (platform-1e, 2026-10-17).** `jq` is not installed on leo or sophie, and nothing is installed for the trial. Every `jq` line runs on the trusted signing host. A box writes raw JSON (`inspect.json`, `key-proof.json`) to a file; the trusted host fetches it over SSM, parses it there, and stages the results (`roster.json`, `rev`, `le`) back on the box over SSM. The box commands read those files with `cat`. A wrong or stale staged roster is refused, because `console-config` and `evidence-key` verify the roster's signature and its revision against `--expect-revision`. |
| - |
| -Where they run: |
| -- **The trusted signing host:** a throwaway dev instance, `carlos-claim-signer`, proposed in `docs/superpowers/notes/claim-trial-inputs/signing-host.md` (root-runner pattern, SSM only, its own role that may read `/carlos/release-key`). It holds `/root/claim-trial` with the inputs, the key in a `0600` tmpfs file at `/run/claim-signer/release.key`, `jq` and the carlos CLI (v0.28.1 for step 5, v0.28.2 from step 6), both checked by sha256. It runs only the signing helpers (`refresh`, `apply_plan`, `phase`) with its instance role; set `KEY=/run/claim-signer/release.key` and `CARLOS=/usr/local/bin/carlos`, and leave out `export AWS_PROFILE=kass`. The release signing key never goes on either box. |
| -- **The operator host (DOCS-T1, profile `kass`):** runs the box helpers below (`ssm_run`, `box_get`, `box_put`, `box_inspect`) and the `jq` parsing for the boxes. Files move between the signer and DOCS-T1 through `s3://carlos-dev-640012953448/claim-signer/<run-id>/`. |
| -- **Leo and sophie:** attended root shells for the box blocks, which use only the platform binary, `systemctl` and the shell. Inputs are staged at the exact paths shown, root-owned, private files `0600`, no symlinks or extra hard links. Staging is a checkpoint, not permission to make up signatures. |
| - |
| -The controller supplies, and Paul approves, the concrete inputs: the signed release, floor and guard artifacts; namespace, pool and label inputs (leo's pool `cohort1`, sophie's matching its real placement); the console identity; the fence role and credentials from step 3; and leo's reviewed host.env changes. Revalidate both instance IDs first. The drafts, with every value that could not be confirmed read-only marked `TO-CONFIRM`, are in `docs/superpowers/notes/claim-trial-inputs/`. |
| - |
| -## Step 5. Roster, enrollment and console enrollment |
| +**Done on 2026-10-18, on v0.28.1 (`3b69c2bd`), each box on its own Paul's go:** |
| +- leo published at 06:10:49: `control/boxes/leo-climbing.json`, 919 bytes. Its verify re-run said "nothing was written". |
| +- sophie published at 06:11:32: `control/boxes/sophie-knitting.json`, 922 bytes. Its verify re-run said "nothing was written". |
| +- Afterwards wakecheck answered 200, both edges were active, and there were no new AccessDenied lines (sophie's two were the known Cost Explorer lines). |
| -Unblocked: v0.28.1 (`3b69c2bd`) is on dev since 2026-10-18 (leo, sophie and dennis current and armed with 0 REFUSING; the dev console on `3b69c2bd`). It needs Paul's approval of the step 5 package (the inputs in `docs/superpowers/notes/claim-trial-inputs/`, the signing host, and the box-record publishes), but not the gap 1 and 2 fixes. |
| - |
| -Paul's go: once for step 5. |
| - |
| -### 5.0 Box identity records |
| +The procedure as run, kept for the record: |
| Each box's signed AWS identity record must be in place at `control/boxes/<box>.json` before the box is invited. They come from `carlos system box publish-record` (v0.28.1, dev only), run once on each box. A missing or refused record is a stop, not permission to make one up. |
| @@ -400,8 +375,51 @@ the record at control/boxes/sophie-knitting.json is valid AWS-signed evidence fo |
| Any other answer is a stop. The `invite-box` dry run in 5.1 then checks the same evidence again, from the signer. |
| +## The signing session: 5.1, 6 and 7 |
| + |
| +Paul's ruling, 2026-10-18: the trusted signing host is created once, after v0.28.2 is on dev, for one session that covers 5.1, step 6 and step 7, so the release key is on a host only once. The trial waits for v0.28.2 before anything past 5.0. |
| + |
| +Order of work: |
| +1. **Open the session:** create the signer (TO-CREATE, Paul's go), install the verified tools, and fetch the key into tmpfs (`signing-host.md`, "Create" and "Getting the key there"). |
| +2. **5.1** roster and enrollment. |
| +3. **Step 6:** arm leo's guard and start the guarded console. |
| +4. **Step 7:** readers, floor, home and leo's key. |
| +5. **Close the session:** shred the key, terminate the signer, delete its role, profile and security group, and run the CloudTrail check ("Closing the signing session", after step 7). |
| + |
| +Nothing else touches the signer between opening and closing: no other work, no other users, no second instance. Each of 5.1, 6 and 7 still has its own Paul's go inside the session. A stop at any point ends the session (see "Stop and hold"). |
| + |
| +Steps 5.1 to 7 use `carlos system claims` from main. Its help text on main: |
| + |
| +``` |
| +usage: carlos system claims <command> [flags] |
| + |
| + inspect --bucket B --aws-region R --public-key K |
| + run --plan FILE --signing-key FILE --public-key K --aws-region R |
| + evidence-key --roster FILE --expect-revision N --bucket B --box BOX --enrollment E --public-key K |
| + fence-credentials --source FILE --bucket B --aws-region R --role ARN |
| + console-config --roster FILE --expect-revision N --bucket B --console-id ID --public-key K [--enable-console-runtime] |
| + console-report --bucket B --aws-region R --expect-revision N --console-id ID --public-key K |
| + install-control |
| + |
| +For commands that write, add --dry-run to validate and print the planned operation without writing. |
| +Run signing phases on the trusted operator host. Run box installers as root. |
| +``` |
| + |
| +`run` takes a JSON plan whose `phase` is one of `initialize`, `configure`, `invite-box`, `enroll-box`, `invite-console`, `enroll-console`, `publish-floor`, `home` or `bind-key`, with `bucket` and `expected_revision`. Every `carlos` command below is quoted from platform-1e's report (run 2, sections A to C; run 11 confirms these command branches are byte-identical on main), and every flag was checked against main's code. |
| + |
| +**No new tools on the boxes (platform-1e, 2026-10-17).** `jq` is not installed on leo or sophie, and nothing is installed for the trial. Every `jq` line runs on the trusted signing host. A box writes raw JSON (`inspect.json`, `key-proof.json`) to a file; the trusted host fetches it over SSM, parses it there, and stages the results (`roster.json`, `rev`, `le`) back on the box over SSM. The box commands read those files with `cat`. A wrong or stale staged roster is refused, because `console-config` and `evidence-key` verify the roster's signature and its revision against `--expect-revision`. |
| + |
| +Where they run: |
| +- **The trusted signing host:** a throwaway dev instance, `carlos-claim-signer`, proposed in `docs/superpowers/notes/claim-trial-inputs/signing-host.md` (root-runner pattern, SSM only, its own role that may read `/carlos/release-key`). It holds `/root/claim-trial` with the inputs, the key in a `0600` tmpfs file at `/run/claim-signer/release.key`, `jq` and the carlos v0.28.2 CLI, both checked by sha256. It runs only the signing helpers (`refresh`, `apply_plan`, `phase`) with its instance role; set `KEY=/run/claim-signer/release.key` and `CARLOS=/usr/local/bin/carlos`, and leave out `export AWS_PROFILE=kass`. The release signing key never goes on either box. |
| +- **The operator host (DOCS-T1, profile `kass`):** runs the box helpers below (`ssm_run`, `box_get`, `box_put`, `box_inspect`) and the `jq` parsing for the boxes. Files move between the signer and DOCS-T1 through `s3://carlos-dev-640012953448/claim-signer/<run-id>/`. |
| +- **Leo and sophie:** attended root shells for the box blocks, which use only the platform binary, `systemctl` and the shell. Inputs are staged at the exact paths shown, root-owned, private files `0600`, no symlinks or extra hard links. Staging is a checkpoint, not permission to make up signatures. |
| + |
| +The controller supplies, and Paul approves, the concrete inputs: the signed release, floor and guard artifacts; namespace, pool and label inputs (leo's pool `cohort1`, sophie's matching its real placement); the console identity; the fence role and credentials from step 3; and leo's reviewed host.env changes. Revalidate both instance IDs first. The drafts, with every value that could not be confirmed read-only marked `TO-CONFIRM`, are in `docs/superpowers/notes/claim-trial-inputs/`. |
| + |
| ### 5.1 Roster and enrollment |
| +Inside the signing session. Paul's go: once for 5.1. |
| + |
| On the trusted signing host, the helpers. The first block is the report's; the second adds the SSM transfer helpers that replace `jq` on the boxes: |
| ```bash |
| @@ -659,6 +677,26 @@ Expected: |
| On failure: hold. A torn publish is resumed with identical bytes. Never erase the floor or substitute another digest. Leo's claim flag stays off. |
| +## Closing the signing session |
| + |
| +Straight after step 7 passes, or at any stop inside the session. Paul's go is not needed to close; closing is always allowed. |
| + |
| +1. On the signer, as root: `shred -u /run/claim-signer/release.key && rmdir /run/claim-signer`. |
| +2. From DOCS-T1: terminate the instance, remove the exchange objects under `claim-signer/<run-id>/` by exact key, then delete the role, instance profile and security group (`signing-host.md`, "Teardown"). |
| +3. CloudTrail check, from DOCS-T1, with the session's start time: |
| + |
| + ```bash |
| + aws --profile kass --region eu-west-1 cloudtrail lookup-events \ |
| + --lookup-attributes AttributeKey=EventName,AttributeValue=GetParameter --start-time <session-start> \ |
| + --query 'Events[].CloudTrailEvent' --output json \ |
| + | jq -r '.[] | fromjson | select(.requestParameters.name == "/carlos/release-key") | [.eventTime, .userIdentity.arn] | @tsv' |
| + aws --profile kass ec2 describe-instances --region eu-west-1 --filters Name=tag:purpose,Values=claim-signer \ |
| + Name=instance-state-name,Values=pending,running,stopping,stopped --query 'Reservations[].Instances[].InstanceId' --output text |
| + aws --profile kass iam get-role --role-name carlos-claim-signer |
| + ``` |
| + |
| + Expected: every `GetParameter` on `/carlos/release-key` since the session started comes from `arn:aws:sts::640012953448:assumed-role/carlos-claim-signer/<the signer's instance id>`, and there are as many as the session made (one per key fetch); no instance listed; `NoSuchEntity` for the role. Anything else is reported on #250 before any further step. |
| + |
| ## Step 7a. Fence-writer credentials |
| Ruled by platform-1e on 2026-10-17: option (a), a one-time SecureString transfer, with two tightenings. This step installs leo's fence-writer credentials (the bootstrap user's access key and the role ARN from step 3) at `/etc/carlos/fence-writer/credentials.json`. |
| @@ -1370,7 +1408,7 @@ This replaces a claims-off rollback for the trial. If anything from step 5 on fa |
| Paul's go: not needed to stop. Stopping is always allowed. Anything after it needs a go. |
| -**Steps 5 to 7.** Nothing claim-mode runs yet. Stop the runbook and change nothing more; leo keeps serving claims off. |
| +**Inside the signing session (5.1 to 7).** Nothing claim-mode runs on leo yet. A stop ends the session: close it at once ("Closing the signing session": shred the key, terminate the signer, delete its role, profile and security group, run the CloudTrail check). Leave leo, sophie, the roster, the floor and the bucket as they are; leo keeps serving claims off. Resuming needs Paul's go for a new signing session, which would put the release key on a host a second time. |
| **Step 8, before the seed starts (8.1, 8.2).** The six services, five triggers and the edge are already persistently masked. Leave every mask and `$WINDOW` as they are. Leave instance-control as it is (running if 8.2 started it). |
| @@ -1465,5 +1503,5 @@ For platform-1e: |
| 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. Box identity records: `carlos system box publish-record` ships in v0.28.1, dev only (step 5.0). The v0.28.1 dev roll comes before step 5. |
| 6. Answered 2026-10-18: platform-1e builds the armed policy signing phase and the published system identity with the protected console install, in v0.28.2 (blocker 5). Step 6 waits for it; step 5 does not. |
| -7. Still open in `docs/superpowers/notes/claim-trial-inputs/README.md`: the operator identity (Paul; suggested default `paul@keymail.dev`), the console enrollment token, the v0.28.1 and v0.28.2 artifacts, and the signing host values. Namespace `platform`, sophie's pool `console` and console ID `dev-console` are confirmed. |
| +7. Still open in `docs/superpowers/notes/claim-trial-inputs/README.md`: the console enrollment token, the v0.28.1 and v0.28.2 artifacts, and the signing host values. Namespace `platform`, sophie's pool `console`, console ID `dev-console` and operator `paul@keymail.dev` are confirmed. |
| 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. |