carlos / platform Public

docs: cohort-1 runbook: tmpfs fence staging, --source - follow-up, v0.28.0

Per platform-1e's ruling on the 7a gap:
- 7a.2 keeps main's fence-credentials --source file, staged on tmpfs at
  /run/carlos-fence-stage/ (directory made by root with umask 077),
  written with install -m 0600 -o root -g root /dev/stdin straight from
  get-parameter --with-decryption, removed by a trap in the same
  subshell on success or failure, and confirmed gone with stat.
- --source - recorded as a follow-up for production cohorts, beside the
  independent writer and key rotation; the open question is removed.
- 7a.1 names the operator principal (user/remote-agent, profile kass)
  and that platform-1e states it when taking 7a.1 to Paul.
- Step 1 names v0.28.0, cut from main 26065378; dev rolls first and the
  trial waits until dev is rolled and checked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 93d1bf92f4c3867647e6a32076d3acc6e3560f03 parent 8b69fe8
1 file changed, +50 −27
  • docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md +50 −27

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	310.870s
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	2.792s
ok  	github.com/carlosframework/platform/cmd/moveprobe	92.222s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.710s
ok  	github.com/carlosframework/platform/internal/activator	251.658s
ok  	github.com/carlosframework/platform/internal/agentsock	0.027s
ok  	github.com/carlosframework/platform/internal/alerting	0.008s
ok  	github.com/carlosframework/platform/internal/analytics	0.021s
ok  	github.com/carlosframework/platform/internal/appconfig	2.447s
ok  	github.com/carlosframework/platform/internal/apperr	0.086s
ok  	github.com/carlosframework/platform/internal/applog	0.141s
ok  	github.com/carlosframework/platform/internal/awsinv	0.005s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.009s
ok  	github.com/carlosframework/platform/internal/boxid	0.864s
?   	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.015s
ok  	github.com/carlosframework/platform/internal/caps	0.017s
ok  	github.com/carlosframework/platform/internal/certwarm	0.010s
ok  	github.com/carlosframework/platform/internal/cgroup	0.698s
ok  	github.com/carlosframework/platform/internal/changelog	0.308s
ok  	github.com/carlosframework/platform/internal/claimactivation	0.033s
ok  	github.com/carlosframework/platform/internal/clamd	0.061s
?   	github.com/carlosframework/platform/internal/cliflag	[no test files]
ok  	github.com/carlosframework/platform/internal/console	407.475s
?   	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.492s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.009s
ok  	github.com/carlosframework/platform/internal/dbverify	0.007s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.003s
ok  	github.com/carlosframework/platform/internal/docsite	0.004s
ok  	github.com/carlosframework/platform/internal/domains	2.400s
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.885s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.009s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.136s
ok  	github.com/carlosframework/platform/internal/gate	1.656s
ok  	github.com/carlosframework/platform/internal/genselect	0.015s
ok  	github.com/carlosframework/platform/internal/geo	0.018s
ok  	github.com/carlosframework/platform/internal/geoip	0.030s
ok  	github.com/carlosframework/platform/internal/historyreadback	0.177s
ok  	github.com/carlosframework/platform/internal/host	173.851s
ok  	github.com/carlosframework/platform/internal/hostactors	0.622s
ok  	github.com/carlosframework/platform/internal/hostbox	2.662s
ok  	github.com/carlosframework/platform/internal/hostclaim	0.563s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.013s
ok  	github.com/carlosframework/platform/internal/hostexec	4.940s
ok  	github.com/carlosframework/platform/internal/hostfence	0.033s
ok  	github.com/carlosframework/platform/internal/hostfiles	10.493s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.016s
ok  	github.com/carlosframework/platform/internal/hostguard	1.039s
ok  	github.com/carlosframework/platform/internal/hostops	1.593s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.016s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.016s
ok  	github.com/carlosframework/platform/internal/hostrelocate	0.462s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.152s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.016s
ok  	github.com/carlosframework/platform/internal/infraview	0.016s
ok  	github.com/carlosframework/platform/internal/instancectl	5.228s
ok  	github.com/carlosframework/platform/internal/instanceexec	2.727s
ok  	github.com/carlosframework/platform/internal/instancefs	0.019s
ok  	github.com/carlosframework/platform/internal/instancekey	0.016s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.020s
ok  	github.com/carlosframework/platform/internal/layout	1.522s
ok  	github.com/carlosframework/platform/internal/lbhealth	0.017s
ok  	github.com/carlosframework/platform/internal/lease	9.833s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.018s
ok  	github.com/carlosframework/platform/internal/ledger	0.017s
ok  	github.com/carlosframework/platform/internal/mail	0.168s
ok  	github.com/carlosframework/platform/internal/maintenance	0.085s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.542s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.088s
ok  	github.com/carlosframework/platform/internal/metrics	0.005s
ok  	github.com/carlosframework/platform/internal/moves	21.069s
ok  	github.com/carlosframework/platform/internal/moves/archive	0.776s
ok  	github.com/carlosframework/platform/internal/objstore	0.193s
ok  	github.com/carlosframework/platform/internal/ops	0.192s
ok  	github.com/carlosframework/platform/internal/park	0.220s
ok  	github.com/carlosframework/platform/internal/privatefile	0.004s
ok  	github.com/carlosframework/platform/internal/provision	35.331s
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.494s
ok  	github.com/carlosframework/platform/internal/relay	7.258s
ok  	github.com/carlosframework/platform/internal/release	21.664s
ok  	github.com/carlosframework/platform/internal/rollout	3.740s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.004s
ok  	github.com/carlosframework/platform/internal/rootaws	0.008s
ok  	github.com/carlosframework/platform/internal/rootparticipation	0.058s
ok  	github.com/carlosframework/platform/internal/rootstate	0.027s
ok  	github.com/carlosframework/platform/internal/runs	0.583s
ok  	github.com/carlosframework/platform/internal/s3compat	0.006s
ok  	github.com/carlosframework/platform/internal/safefile	0.004s
ok  	github.com/carlosframework/platform/internal/schedule	2.516s
ok  	github.com/carlosframework/platform/internal/seeds	0.011s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	9.415s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	3.532s
ok  	github.com/carlosframework/platform/internal/status	0.042s
ok  	github.com/carlosframework/platform/internal/steering	0.006s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.291s
ok  	github.com/carlosframework/platform/internal/svcrole	0.004s
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.578s
=== 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: downloading https://github.com/minio/minio/releases/download/RELEASE.2025-09-07T16-13-09Z/minio.linux-arm64.RELEASE.2025-09-07T16-13-09Z
  minio: downloaded, digest verified
  mc: downloading https://github.com/minio/mc/releases/download/RELEASE.2025-08-13T08-35-41Z/mc.linux-arm64.RELEASE.2025-08-13T08-35-41Z
  mc: downloaded, 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.031s
=== RUN   TestT4StorageProbeMinio
2026/10/17 20:46:43 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DF6C12822B921A, 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.023s
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 b01d169d7..7a2f59066 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,7 +191,7 @@ Pass: all twelve are root-owned regular files in `/etc/systemd/system`; the edge
## Blockers
-1. **Release.** No release contains 26065378 (Plan 14 Parts 2 and 3, the activation tools) or e8e961dd (the empty-app writer fix). Step 1.
+1. **Release.** No published release yet contains 26065378 (Plan 14 Parts 2 and 3, the activation tools) or e8e961dd (the empty-app writer fix). v0.28.0 is being cut from 26065378. Step 1.
2. **Empty app segment.** 267 objects under `apps/bab//` (P5). Source: leo's app-less `proof*` routes, removed (2026-10-16). Cleanup waits for the e8e961dd release to reach leo and for Paul's approval. Step 2.
3. **IAM.** Leo's role lacks the version actions, and dev has neither the deployment bucket policy nor a fence-writer role (P2). Step 3.
@@ -199,20 +199,21 @@ Pass: all twelve are root-owned regular files in `/etc/systemd/system`; the edge
Paul deferred these for this trial only. They are not blockers here, and they must be closed before any non-trial cohort:
- **Plan 15's independent claim writer.** Leo's instance role can write `control/hosts/`, so the box's own credentials could rewrite its claims.
+- **`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.)
## Step 1. Release and roll, claims off
-What: cut a release from main containing 26065378 and e8e961dd, and roll it with every claim switch unset. Dev goes first. Flagship does not need this release before dev's window (Paul, 2026-10-16); its roll is separate.
+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.
Paul's go: to cut the release, then to roll dev.
-1. Cut the release by the README section "Releasing the platform". Check `gh release list -R carlosframework/releases` and `git ls-remote --tags origin` before choosing the number.
+1. Cut v0.28.0 from 26065378 by the README section "Releasing the platform" (in progress on 2026-10-17). Confirm with `gh release list -R carlosframework/releases` that v0.28.0 is published before rolling.
2. Roll the dev boxes on the new road only:
```bash
- carlos system update --console https://platform.dev.carlosframework.com --channel stable --version vX.Y.Z --yes
- carlos system update --console https://platform.dev.carlosframework.com --channel canary --version vX.Y.Z --yes
+ carlos system update --console https://platform.dev.carlosframework.com --channel stable --version v0.28.0 --yes
+ carlos system update --console https://platform.dev.carlosframework.com --channel canary --version v0.28.0 --yes
carlos system status --console https://platform.dev.carlosframework.com
```
@@ -221,9 +222,9 @@ Paul's go: to cut the release, then to roll dev.
4. Check dev end to end: wake `wakecheck` on sophie through DNS (claims are still off, and wakecheck is still on both boxes).
Expected:
-- `system status` shows all three dev boxes `current` at vX.Y.Z;
+- `system status` shows all three dev boxes `current` at v0.28.0;
- `curl -sI https://platform.dev.carlosframework.com/ | grep -i x-carlos-version` shows the release commit;
-- on leo, `systemctl show carlos-instance-control.service -p ActiveEnterTimestamp` is later than the update, and `/opt/carlos/carlos version` prints vX.Y.Z;
+- on leo, `systemctl show carlos-instance-control.service -p ActiveEnterTimestamp` is later than the update, and `/opt/carlos/carlos version` prints v0.28.0;
- `/opt/carlos/carlos system claims help` on leo prints main's usage, listing `inspect`, `run`, `evidence-key`, `fence-credentials`, `console-config`, `console-report` and `install-control`;
- the wake answers 200.
@@ -602,7 +603,9 @@ Residual exposure: between 7a.2's write and its delete, leo's role can read the
Paul's go: separate gate, for creating the key, its policy and leo's temporary read grant.
-Dev's infrastructure is OpenTofu in `platform-infrastructure/dev`, so the key is declared there. Add this file on a reviewed branch (the operator ARN is the principal behind profile `kass` today, `arn:aws:iam::640012953448:user/remote-agent`; confirm it at approval, and it must also be the principal that runs `tofu apply`, or the key policy lockout check refuses the key):
+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:
```hcl
# platform-infrastructure/dev/cohort1-fence-kms.tf
@@ -711,29 +714,50 @@ box_put "$LEO" fence-role-arn /root/claim-trial/fence-role-arn
Expected: version `1`, an 8-digit fingerprint to compare in part 2, and `staged` for the role ARN (an ARN, not a secret).
-**2. On leo, read it without echoing it, straight into a protected file, and install it.** Main's `carlos system claims fence-credentials` has no stdin option: `--source` must be a root-owned regular file with mode `0600` or `0400`. So the value goes from `aws ssm get-parameter` straight into such a file, never to the terminal, then `fence-credentials` installs it. The AWS CLI on leo (v2, `/usr/bin/aws`) uses leo's instance role; do not set a profile. In the attended root shell on leo:
+**2. On leo, read it without echoing it into a tmpfs staging file, and install it.** For this trial main's `carlos system claims fence-credentials --source FILE` stays as it is (platform-1e, 2026-10-17): `--source` must be a root-owned regular file with mode `0600` or `0400`, and there is no stdin option. The staging file is held to four rules:
+- **tmpfs only:** it lives under `/run/carlos-fence-stage/`, never on disk. Root creates the directory with umask 077.
+- **Never readable by others:** `install -m 0600 -o root -g root /dev/stdin` writes it straight from `aws ssm get-parameter --with-decryption`, so it never exists with wider modes, and the value never reaches the terminal.
+- **Always deleted:** a `trap` in the same shell removes it as soon as `fence-credentials` returns, on success or failure.
+- **Checked:** `stat` confirms it is gone before anything else happens.
+
+The AWS CLI on leo (v2, `/usr/bin/aws`) uses leo's instance role; do not set a profile. In the attended root shell on leo, run the block as one subshell, so the `trap` fires when it ends however it ends:
```bash
-set -euo pipefail
-umask 077
-cd /root/claim-trial
-test ! -e fence-credentials.json
-aws --region eu-west-1 ssm get-parameter --name /carlos-staging/fence/carlos-dev-ie-b --with-decryption \
- --query Parameter.Value --output text > fence-credentials.json
-stat -c '%U %a %h %s' fence-credentials.json
-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
-FENCE_ROLE=$(cat fence-role-arn)
-/opt/carlos/carlos system claims fence-credentials --source /root/claim-trial/fence-credentials.json \
- --bucket carlos-dev-640012953448 --aws-region eu-west-1 --role "$FENCE_ROLE" --dry-run
-/opt/carlos/carlos system claims fence-credentials --source /root/claim-trial/fence-credentials.json \
- --bucket carlos-dev-640012953448 --aws-region eu-west-1 --role "$FENCE_ROLE"
-rm -- /root/claim-trial/fence-credentials.json
+(
+ set -euo pipefail
+ umask 077
+ STAGE=/run/carlos-fence-stage
+ SRC="$STAGE/credentials.json"
+ 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 \
+ --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"
+ FENCE_ROLE=$(cat /root/claim-trial/fence-role-arn)
+ /opt/carlos/carlos system claims fence-credentials --source "$SRC" \
+ --bucket carlos-dev-640012953448 --aws-region eu-west-1 --role "$FENCE_ROLE" --dry-run
+ /opt/carlos/carlos system claims fence-credentials --source "$SRC" \
+ --bucket carlos-dev-640012953448 --aws-region eu-west-1 --role "$FENCE_ROLE"
+)
+echo "subshell exit=$?"
+if stat -- /run/carlos-fence-stage/credentials.json >/dev/null 2>&1; then
+ echo "STAGING FILE STILL PRESENT: rm -f -- /run/carlos-fence-stage/credentials.json, then stop"
+else
+ echo "staging file gone"
+fi
+rmdir -- /run/carlos-fence-stage
stat -c '%U %a %h' /etc/carlos/fence-writer/credentials.json
```
-Expected: `root 600 1 <size>`; the same fingerprint as part 1; both `fence-credentials` runs print their JSON summary (`"target": "/etc/carlos/fence-writer/credentials.json"`, `"mode": "0600"`); the staging file is removed; the installed file is `root 600 1`. Nothing prints a credential value. If anything fails, remove the staging file anyway, then go on to parts 3 to 5 before investigating.
+Expected:
+- `root 600 1 <size>` for the staging file, and the same fingerprint as part 1;
+- both `fence-credentials` runs print their JSON summary (`"target": "/etc/carlos/fence-writer/credentials.json"`, `"mode": "0600"`);
+- `subshell exit=0`, then `staging file gone`;
+- the installed file `root 600 1`.
-Gap for platform-1e: because `fence-credentials` has no stdin source, the plaintext sits in a root-only staging file (`/root/claim-trial`, `0700`) for the seconds between the read and the install. A `--source -` that reads standard input would remove that file.
+Nothing prints a credential value. If anything fails, the `trap` has already removed the staging file: confirm `staging file gone`, then go on to parts 3 to 5 before investigating.
**3. Delete the parameter immediately.** On the trusted host:
@@ -1311,9 +1335,8 @@ 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).
+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.
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. `carlos system claims fence-credentials` reads only a `--source` file. Should it also read standard input (`--source -`), so step 7a.2 needs no plaintext staging file on leo?