docs: cohort-1 runbook 7a on /carlos/cohort1, PR #45 plan and simulation; drift owed
- Fence parameter moves to /carlos/cohort1/fence/carlos-dev-ie-b (Paul, option A). The dedicated key carries the separation; sophie can fetch the ciphertext through /carlos/* (accepted), dev-uk cannot, and both are denied kms:Decrypt. - 7a.1 quotes the file on platform-infrastructure PR #45 and its targeted plan (3 to add, 0 to change, 0 to destroy, on main f609434 with #44 merged), plus the pre-apply policy simulation and lockout check. Add and remove plans are targeted at the three resources. - New "Owed before production cohorts": the dev module drift (15 adds, 1 change, including leo's object-version actions and the SSM scope policy), with its own plan and Paul's approval later; not part of 7a. Open question on step 3's overlap with it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+65
−22
docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md+65 −22
CI passed — 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
ok github.com/carlosframework/platform 293.213s
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 2.839s
ok github.com/carlosframework/platform/cmd/moveprobe 92.155s
ok github.com/carlosframework/platform/cmd/zddprobe 0.700s
ok github.com/carlosframework/platform/internal/activator 251.387s
ok github.com/carlosframework/platform/internal/agentsock 0.015s
ok github.com/carlosframework/platform/internal/alerting 0.003s
ok github.com/carlosframework/platform/internal/analytics 0.024s
ok github.com/carlosframework/platform/internal/appconfig 2.458s
ok github.com/carlosframework/platform/internal/apperr 0.070s
ok github.com/carlosframework/platform/internal/applog 0.125s
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.638s
? 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.009s
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.695s
ok github.com/carlosframework/platform/internal/changelog 0.305s
ok github.com/carlosframework/platform/internal/claimactivation 0.026s
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 400.604s
? 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.439s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.009s
ok github.com/carlosframework/platform/internal/dbverify 0.004s
ok github.com/carlosframework/platform/internal/dnsrec 0.002s
ok github.com/carlosframework/platform/internal/docsite 0.004s
ok github.com/carlosframework/platform/internal/domains 2.384s
ok github.com/carlosframework/platform/internal/doorkey 0.006s
ok github.com/carlosframework/platform/internal/economics 0.003s
ok github.com/carlosframework/platform/internal/edge 44.827s
? github.com/carlosframework/platform/internal/edgeproof [no test files]
ok github.com/carlosframework/platform/internal/edgesfile 0.007s
ok github.com/carlosframework/platform/internal/fleetapi 0.129s
ok github.com/carlosframework/platform/internal/gate 1.656s
ok github.com/carlosframework/platform/internal/genselect 0.014s
ok github.com/carlosframework/platform/internal/geo 0.017s
ok github.com/carlosframework/platform/internal/geoip 0.031s
ok github.com/carlosframework/platform/internal/historyreadback 0.150s
ok github.com/carlosframework/platform/internal/host 173.912s
ok github.com/carlosframework/platform/internal/hostactors 0.629s
ok github.com/carlosframework/platform/internal/hostbox 2.685s
ok github.com/carlosframework/platform/internal/hostclaim 0.544s
ok github.com/carlosframework/platform/internal/hostevidence 0.006s
ok github.com/carlosframework/platform/internal/hostexec 4.735s
ok github.com/carlosframework/platform/internal/hostfence 0.038s
ok github.com/carlosframework/platform/internal/hostfiles 10.400s
ok github.com/carlosframework/platform/internal/hostfloor 0.017s
ok github.com/carlosframework/platform/internal/hostguard 0.993s
ok github.com/carlosframework/platform/internal/hostops 1.587s
? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files]
ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.004s
ok github.com/carlosframework/platform/internal/hostquarantine 0.003s
ok github.com/carlosframework/platform/internal/hostrelocate 0.455s
ok github.com/carlosframework/platform/internal/hostresolve 0.139s
ok github.com/carlosframework/platform/internal/hostwrite 0.002s
ok github.com/carlosframework/platform/internal/infraview 0.002s
ok github.com/carlosframework/platform/internal/instancectl 5.209s
ok github.com/carlosframework/platform/internal/instanceexec 2.746s
ok github.com/carlosframework/platform/internal/instancefs 0.018s
ok github.com/carlosframework/platform/internal/instancekey 0.014s
ok github.com/carlosframework/platform/internal/instanceupdates 0.006s
ok github.com/carlosframework/platform/internal/layout 1.512s
ok github.com/carlosframework/platform/internal/lbhealth 0.002s
ok github.com/carlosframework/platform/internal/lease 9.806s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.007s
ok github.com/carlosframework/platform/internal/ledger 0.013s
ok github.com/carlosframework/platform/internal/mail 0.151s
ok github.com/carlosframework/platform/internal/maintenance 0.084s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.473s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.077s
ok github.com/carlosframework/platform/internal/metrics 0.004s
ok github.com/carlosframework/platform/internal/moves 20.869s
ok github.com/carlosframework/platform/internal/moves/archive 0.813s
ok github.com/carlosframework/platform/internal/objstore 0.181s
ok github.com/carlosframework/platform/internal/ops 0.164s
ok github.com/carlosframework/platform/internal/park 0.194s
ok github.com/carlosframework/platform/internal/privatefile 0.003s
ok github.com/carlosframework/platform/internal/provision 17.180s
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.376s
ok github.com/carlosframework/platform/internal/relay 7.224s
ok github.com/carlosframework/platform/internal/release 21.129s
ok github.com/carlosframework/platform/internal/rollout 3.593s
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.057s
ok github.com/carlosframework/platform/internal/rootstate 0.026s
ok github.com/carlosframework/platform/internal/runs 0.574s
ok github.com/carlosframework/platform/internal/s3compat 0.004s
ok github.com/carlosframework/platform/internal/safefile 0.004s
ok github.com/carlosframework/platform/internal/schedule 2.469s
ok github.com/carlosframework/platform/internal/seeds 0.011s
ok github.com/carlosframework/platform/internal/sim/hostlife 8.354s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 3.760s
ok github.com/carlosframework/platform/internal/status 0.039s
ok github.com/carlosframework/platform/internal/steering 0.005s
ok github.com/carlosframework/platform/internal/stopevidence 0.232s
ok github.com/carlosframework/platform/internal/svcrole 0.002s
ok github.com/carlosframework/platform/internal/telemetry 0.003s
ok github.com/carlosframework/platform/internal/termfence 0.003s
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.549s
=== 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.021s
=== RUN TestT4StorageProbeMinio
2026/10/17 23:26:28 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DF74CA45CE9FFF, 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.00s)
PASS
ok github.com/carlosframework/platform/internal/host 0.019s
minio contract: PASS
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 7a2f59066..1d8cf4206 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 |
| @@ -202,6 +202,10 @@ Paul deferred these for this trial only. They are not blockers here, and they mu |
| - **`fence-credentials --source -`.** For the trial, the credentials pass through a tmpfs staging file under `/run` (step 7a.2). Reading standard input instead is a follow-up for production cohorts (platform-1e is filing it on amadan). |
| - **Key rotation.** The release signing key is not rotated for the trial, and no rotation schedule is set for the fence-writer bootstrap keys. (The separate exposure, box roles reading `/carlos/release-key`, is resolved; see P2.) |
| +## Owed before production cohorts |
| + |
| +- **Dev module drift.** A whole-root `tofu plan` of `platform-infrastructure/dev` on 2026-10-17 showed 15 adds and 1 change that come from the platform modules (pinned `?ref=main`) moving ahead of dev: termination SQS queues and EventBridge rules for both IE boxes, the deployment bucket's lifecycle rule and its box-records bucket policy, leo's object-version actions (`host_buckets` gains `PullDoorObjectVersions`, `s3:DeleteObjectVersion` and `PullDoorVersionList`), and an SSM parameter scope policy on both IE roles (a deny on parameter reads outside `/carlos/*`). It is owed: its own plan and Paul's approval later, before production cohorts. It is not part of step 7a, whose plan is targeted at its own three resources. Step 3's IAM overlaps it (see open question 3). |
| + |
| ## Step 1. Release and roll, claims off |
| What: v0.28.0, cut from main at 26065378 (it contains Plan 14 Parts 2 and 3, the activation tools and e8e961dd), rolled with every claim switch unset. Dev rolls first. The trial waits until dev is rolled and checked. Flagship does not need this release before dev's window (Paul, 2026-10-16); its roll is separate. |
| @@ -592,8 +596,8 @@ Scope: the independent claim writer (Plan 15) is deferred for this trial. This s |
| Rules for the whole step: |
| - The credential values never appear in an SSM command parameter, a command line, shell history, a log, terminal output or CloudTrail request data. SSM command history and CloudTrail store command parameters, so `box_put` is never used for these credentials. |
| -- The parameter lives outside `/carlos/`, at `/carlos-staging/fence/carlos-dev-ie-b`. Every dev box role may read `/carlos/*`, so under `/carlos/` sophie's role could read leo's credentials. |
| -- It is encrypted with a dedicated KMS key whose key policy lets only leo's role (`carlos-dev-host-b`) decrypt. A grant on the parameter path alone is not enough. |
| +- The parameter is `/carlos/cohort1/fence/carlos-dev-ie-b` (Paul, 2026-10-18, option A). The dedicated KMS key carries the separation: only leo's role (`carlos-dev-host-b`) may decrypt, the encryption context binds decryption to that parameter's ARN, and an explicit deny covers everyone else. A grant on the parameter path alone would not be enough. |
| +- Every dev box role may read `/carlos/*`, so sophie's role can fetch the parameter's ciphertext (dev-uk's role cannot: its parameter scope denies eu-west-1). Both are denied `kms:Decrypt`, so a fetch with decryption fails. Paul accepted the ciphertext exposure. |
| Why not option (b), leo's role fetching the credentials whenever it needs them: leo's instance role is reachable from any process on leo through the instance metadata service (IMDS), including the carlos agent. A standing grant would let the agent fetch the root fence writer's credentials at will, which removes the separation between the root fence writer and the box role (Plan 9 A3, #250 M-2). With (a), the parameter exists for minutes, leo's read grant is removed straight after, and the only remaining copy is root-owned and `0600` on leo. |
| @@ -605,15 +609,34 @@ Paul's go: separate gate, for creating the key, its policy and leo's temporary r |
| The operator principal in the key policy is `arn:aws:iam::640012953448:user/remote-agent`, the user behind profile `kass`. platform-1e will state this when taking 7a.1 to Paul. It must also be the principal that runs `tofu apply`, or the key policy lockout check refuses the key. |
| -Dev's infrastructure is OpenTofu in `platform-infrastructure/dev`, so the key is declared there. Add this file on a reviewed branch: |
| +Dev's infrastructure is OpenTofu in `platform-infrastructure/dev`, so the key is declared there. The file is on branch `claim-cohort1-fence-kms`, PR carlosframework/platform-infrastructure#45 (opened 2026-10-18 on Paul's go for a plan and PR only; not applied). Its content: |
| ```hcl |
| # platform-infrastructure/dev/cohort1-fence-kms.tf |
| -# Dev cohort-1 mechanics trial only. Remove this file after step 7a.2. |
| +# Dev cohort-1 mechanics trial only (host-claim activation on |
| +# carlos-dev-ie-b / leo-climbing). Runbook step 7a.1 in carlosframework/platform, |
| +# docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md. |
| +# |
| +# A dedicated KMS key for the one-time transfer of leo's fence-writer |
| +# credentials through a SecureString at /carlos/cohort1/fence/carlos-dev-ie-b. |
| +# Every dev box role may read /carlos/*, so sophie's and dev-uk's roles can |
| +# fetch the ciphertext; that is accepted (Paul, 2026-10-18), because the key |
| +# carries the separation: only leo's role may decrypt, only for that |
| +# parameter's encryption context, and an explicit deny covers everyone else. |
| +# There is no account-root statement, so IAM policies alone grant nothing on |
| +# this key. The operator administers and encrypts but cannot decrypt. |
| +# |
| +# leo's role already reads /carlos/* through its host_buckets policy; the |
| +# explicit one-parameter grant below keeps reading this parameter tied to the |
| +# trial, and still holds once the module's SSM parameter scope (a deny outside |
| +# /carlos/*) is applied. |
| +# |
| +# Remove this file after step 7a.2: that deletes the alias and leo's read |
| +# grant and schedules the key's deletion (7-day window). |
| locals { |
| - cohort1_fence_param = "arn:aws:ssm:eu-west-1:640012953448:parameter/carlos-staging/fence/carlos-dev-ie-b" |
| - cohort1_leo_role_arn = "arn:aws:iam::640012953448:role/carlos-dev-host-b" |
| - cohort1_operator_arns = ["arn:aws:iam::640012953448:user/remote-agent"] |
| + cohort1_fence_param = "arn:aws:ssm:eu-west-1:640012953448:parameter/carlos/cohort1/fence/carlos-dev-ie-b" |
| + cohort1_leo_role_arn = "arn:aws:iam::640012953448:role/carlos-dev-host-b" |
| + cohort1_operator_arns = ["arn:aws:iam::640012953448:user/remote-agent"] |
| } |
| resource "aws_kms_key" "cohort1_fence" { |
| @@ -672,13 +695,16 @@ resource "aws_iam_role_policy" "cohort1_fence_read" { |
| The key policy has no statement for the account root, so IAM policies alone grant nothing on this key: only the listed operator administers and encrypts, and only leo's role decrypts, and only for this one parameter (SSM passes the parameter ARN as encryption context). The operator has no `kms:Decrypt`. Leo's role does not otherwise reach the parameter, because its existing grant covers `parameter/carlos/*` only. |
| +The plan is targeted, because the dev root carries unrelated module drift (owed separately, see "Owed before production cohorts"). It was run on 2026-10-18 from the PR branch, on main `f609434` (which includes #44, the release-key deny), and saved so the apply uses exactly this plan: |
| + |
| ```bash |
| cd platform-infrastructure/dev |
| -tofu plan -out=cohort1-fence.plan |
| -tofu apply cohort1-fence.plan |
| +tofu plan -target=aws_kms_key.cohort1_fence -target=aws_kms_alias.cohort1_fence \ |
| + -target=aws_iam_role_policy.cohort1_fence_read -out=cohort1-fence-targeted.tfplan |
| +tofu apply cohort1-fence-targeted.tfplan |
| ``` |
| -Expected plan: exactly three additions (`aws_kms_key.cohort1_fence`, `aws_kms_alias.cohort1_fence`, `aws_iam_role_policy.cohort1_fence_read`) and nothing else. Stop and ask on any other change. |
| +Expected plan, and the result on 2026-10-18: `Plan: 3 to add, 0 to change, 0 to destroy.` (`aws_iam_role_policy.cohort1_fence_read`, `aws_kms_alias.cohort1_fence`, `aws_kms_key.cohort1_fence`). Stop and ask on any other change. The apply waits for Paul's go against that exact plan. |
| Checks after apply, read-only, from the operator host: |
| @@ -686,16 +712,31 @@ Checks after apply, read-only, from the operator host: |
| for r in carlos-dev-host-b carlos-dev-host carlos-dev-host-uk; do |
| echo "$r $(aws --profile kass iam simulate-principal-policy \ |
| --policy-source-arn arn:aws:iam::640012953448:role/$r --action-names ssm:GetParameter \ |
| - --resource-arns arn:aws:ssm:eu-west-1:640012953448:parameter/carlos-staging/fence/carlos-dev-ie-b \ |
| + --resource-arns arn:aws:ssm:eu-west-1:640012953448:parameter/carlos/cohort1/fence/carlos-dev-ie-b \ |
| --query 'EvaluationResults[0].EvalDecision' --output text)" |
| done |
| aws --profile kass --region eu-west-1 kms get-key-policy --key-id alias/cohort1-fence-leo --policy-name default \ |
| --query Policy --output text | python3 -c 'import json,sys;print([(s["Sid"],s["Effect"]) for s in json.load(sys.stdin)["Statement"]])' |
| ``` |
| -Expected: `carlos-dev-host-b allowed`, the other two `implicitDeny`; the three statement IDs as above. |
| +Expected: `carlos-dev-host-b allowed`; `carlos-dev-host allowed` (ciphertext only, accepted); `carlos-dev-host-uk explicitDeny`; the three statement IDs as above. |
| + |
| +Simulated before the apply, on 2026-10-18 (method in PR #45: `simulate-principal-policy` on the real roles for SSM; `simulate-custom-policy` with the planned key policy as the resource policy for KMS, an IAM-user stand-in in `Principal`, the real role ARN as `aws:PrincipalArn` context): |
| + |
| +| Principal | Action | Result | |
| +|---|---|---| |
| +| leo, with the planned grant | `ssm:GetParameter` | allowed | |
| +| leo, context = the trial parameter | `kms:Decrypt` | allowed | |
| +| leo, context = another parameter | `kms:Decrypt` | implicitDeny | |
| +| sophie | `ssm:GetParameter` | allowed (ciphertext only) | |
| +| sophie | `kms:Decrypt` | explicitDeny | |
| +| dev-uk | `ssm:GetParameter` | explicitDeny | |
| +| dev-uk | `kms:Decrypt` | explicitDeny | |
| +| `remote-agent` | `kms:Decrypt` | explicitDeny | |
| +| `remote-agent` | `kms:Encrypt` | allowed | |
| +| `remote-agent` | `ScheduleKeyDeletion`, `CancelKeyDeletion`, `PutKeyPolicy`, `DescribeKey`, `GetKeyPolicy` | allowed (no lockout) | |
| -Rollback: remove `cohort1-fence-kms.tf` and run `tofu plan` and `tofu apply`. That deletes the alias and leo's read grant and schedules the key's deletion (7-day window). Until the window ends, `aws --profile kass --region eu-west-1 kms cancel-key-deletion --key-id <key-id>` can undo it. |
| +Rollback: remove `cohort1-fence-kms.tf` and run `tofu plan -target=aws_kms_key.cohort1_fence -target=aws_kms_alias.cohort1_fence -target=aws_iam_role_policy.cohort1_fence_read` and `tofu apply` of that plan. That deletes the alias and leo's read grant and schedules the key's deletion (7-day window). Until the window ends, `aws --profile kass --region eu-west-1 kms cancel-key-deletion --key-id <key-id>` can undo it. |
| ### 7a.2 Transfer, install, delete, revoke |
| @@ -705,7 +746,7 @@ Paul's go: separate, once 7a.1 passes. Do the five parts back to back, in one at |
| ```bash |
| cd /root/claim-trial |
| -aws --profile kass --region eu-west-1 ssm put-parameter --name /carlos-staging/fence/carlos-dev-ie-b \ |
| +aws --profile kass --region eu-west-1 ssm put-parameter --name /carlos/cohort1/fence/carlos-dev-ie-b \ |
| --type SecureString --key-id alias/cohort1-fence-leo --value file:///root/claim-trial/fence-credentials.json \ |
| --query Version --output text |
| python3 -c 'import json,sys,hashlib;print(hashlib.sha256(json.dumps(json.load(open(sys.argv[1])),sort_keys=True).encode()).hexdigest()[:8])' fence-credentials.json |
| @@ -731,7 +772,7 @@ The AWS CLI on leo (v2, `/usr/bin/aws`) uses leo's instance role; do not set a p |
| install -d -m 0700 -o root -g root "$STAGE" |
| test ! -e "$SRC" |
| trap 'rm -f -- "$SRC"' EXIT |
| - aws --region eu-west-1 ssm get-parameter --name /carlos-staging/fence/carlos-dev-ie-b --with-decryption \ |
| + aws --region eu-west-1 ssm get-parameter --name /carlos/cohort1/fence/carlos-dev-ie-b --with-decryption \ |
| --query Parameter.Value --output text | install -m 0600 -o root -g root /dev/stdin "$SRC" |
| stat -c '%U %a %h %s' "$SRC" |
| python3 -c 'import json,sys,hashlib;print(hashlib.sha256(json.dumps(json.load(open(sys.argv[1])),sort_keys=True).encode()).hexdigest()[:8])' "$SRC" |
| @@ -762,31 +803,32 @@ Nothing prints a credential value. If anything fails, the `trap` has already rem |
| **3. Delete the parameter immediately.** On the trusted host: |
| ```bash |
| -aws --profile kass --region eu-west-1 ssm delete-parameter --name /carlos-staging/fence/carlos-dev-ie-b |
| +aws --profile kass --region eu-west-1 ssm delete-parameter --name /carlos/cohort1/fence/carlos-dev-ie-b |
| ``` |
| **4. Verify it is gone.** |
| ```bash |
| aws --profile kass --region eu-west-1 ssm describe-parameters \ |
| - --parameter-filters Key=Name,Values=/carlos-staging/fence/carlos-dev-ie-b --query 'Parameters[].Name' --output text |
| + --parameter-filters Key=Name,Values=/carlos/cohort1/fence/carlos-dev-ie-b --query 'Parameters[].Name' --output text |
| ``` |
| Expected: empty output. Deleting a parameter removes all its versions and history. |
| -**5. Straight after, remove leo's grant and schedule the key's deletion.** On the reviewed branch, remove `cohort1-fence-kms.tf`, then: |
| +**5. Straight after, remove leo's grant and schedule the key's deletion.** On the reviewed branch, remove `cohort1-fence-kms.tf`, then (targeted, as for the add): |
| ```bash |
| cd platform-infrastructure/dev |
| -tofu plan -out=cohort1-fence-remove.plan |
| +tofu plan -target=aws_kms_key.cohort1_fence -target=aws_kms_alias.cohort1_fence \ |
| + -target=aws_iam_role_policy.cohort1_fence_read -out=cohort1-fence-remove.plan |
| tofu apply cohort1-fence-remove.plan |
| aws --profile kass iam simulate-principal-policy --policy-source-arn arn:aws:iam::640012953448:role/carlos-dev-host-b \ |
| - --action-names ssm:GetParameter --resource-arns arn:aws:ssm:eu-west-1:640012953448:parameter/carlos-staging/fence/carlos-dev-ie-b \ |
| + --action-names ssm:GetParameter --resource-arns arn:aws:ssm:eu-west-1:640012953448:parameter/carlos/cohort1/fence/carlos-dev-ie-b \ |
| --query 'EvaluationResults[0].EvalDecision' --output text |
| aws --profile kass --region eu-west-1 kms describe-key --key-id <key-id> --query KeyMetadata.KeyState --output text |
| ``` |
| -Expected: the plan removes exactly the three resources; `implicitDeny`; `PendingDeletion`. If `tofu` cannot run straight away, do the same by hand and reconcile the branch afterwards: |
| +Expected: the plan removes exactly the three resources; `allowed` (leo's role still reads `/carlos/*` through `host_buckets`, but the parameter is gone and decryption is no longer possible); `PendingDeletion`. If `tofu` cannot run straight away, do the same by hand and reconcile the branch afterwards: |
| ```bash |
| aws --profile kass iam delete-role-policy --role-name carlos-dev-host-b --policy-name cohort1-fence-read |
| @@ -1335,8 +1377,9 @@ Not part of this trial (Paul, 2026-10-16). dennis-stenciling is placement `uk`, |
| ## Open questions |
| -Answered: dev-uk is out of this trial; the console is enrolled by the activation tools; the independent writer and key rotation are accepted trial gaps; box roles no longer read `/carlos/release-key`; the home name is `ie`; the `proof*` routes are removed; step 4 is approved; flagship does not need the release before dev's window; the automated offline window is replaced by the manual one; `jq` runs only on the trusted host and nothing is installed on the boxes; `install-control` runs once, before `journal-migrate`; the usage and docs edits for the removed commands are confirmed by Paul and on main. Fence credentials go by option (a): a one-time SecureString outside `/carlos/`, under a dedicated KMS key only leo's role can use, deleted and revoked straight after (step 7a); on leo it passes only through a tmpfs staging file under `/run`, deleted by a `trap`; `--source -` is a follow-up for production cohorts. |
| +Answered: dev-uk is out of this trial; the console is enrolled by the activation tools; the independent writer and key rotation are accepted trial gaps; box roles no longer read `/carlos/release-key`; the home name is `ie`; the `proof*` routes are removed; step 4 is approved; flagship does not need the release before dev's window; the automated offline window is replaced by the manual one; `jq` runs only on the trusted host and nothing is installed on the boxes; `install-control` runs once, before `journal-migrate`; the usage and docs edits for the removed commands are confirmed by Paul and on main. Fence credentials go by option (a): a one-time SecureString at `/carlos/cohort1/fence/carlos-dev-ie-b`, under a dedicated KMS key only leo's role can use, deleted and revoked straight after (step 7a); on leo it passes only through a tmpfs staging file under `/run`, deleted by a `trap`; `--source -` is a follow-up for production cohorts. |
| 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. Step 3 expects leo's object-version actions and the deployment bucket's box-records policy, which are part of the owed module drift. Are they applied for the trial (as part of step 3, with their own plan) or deferred with the rest of the drift? |