carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

Instance key provision (L21): plan report, rev 2

Status: rev 2 of the plan is written and committed. It answers Astra's round 1 (CODEX-PLAN-INSTANCE-KEY-R1.md). Nothing is pushed.

  • Branch: instance-key-build (cut from origin/main 0901e835, with Astra's review cherry-picked as 55b22cb5), worktree /home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-key-provision
  • Plan: docs/superpowers/plans/2026-10-10-instance-key-provision.md, rev 2, commit f2c8149c
  • Tasks: 7, then a final verification step. Rev 1 had 5. The new ones are the shared rule (internal/instancekey, Task 1) and the adoption verb (Task 5).

Dispositions

| # | Finding | Disposition | |---|---|---| | 1 | O2 accepts a platform key the app may not use | Accepted: key-source evidence (a), (b), (c); new adoption verb and record; O2' | | 2 | A failed sync becomes success on retry | Accepted: every ensure syncs, present key included; the claim early return syncs too | | 3 | Nested backfill fails with EINVAL | Accepted: mode 0 without O_CREAT; nested backfill test | | 4 | An empty override defeats a configured key | Accepted: one effective-key rule, applied by the mint and by mergeEnv; tested through Start | | 5 | O2 refuses configured keys | Accepted: appconfig.ConfiguredInstanceKey, O2' source (c), relocation test | | 6 | The rollout's merge option | Declined: squash on the hub is Paul's rule; merging is Paul's call | | 7 | Question 5's premise | Accepted: corrected (ruling 16) and pinned by a test | | 8 | Coverage gaps (flat seed, nested seed, real restore) | Accepted: three tests | | 9 | O1's fake proves ordering only | Accepted, moved to O1''s provider tests | | 10 | Legacy durability is disk plus archives | Acknowledged, no change | | Q1-Q4 | Paul's open questions | Provisional defaults, marked as not yet confirmed by Paul |

Counts: 8 accepted, 1 declined, 1 acknowledged with no change; 4 provisional defaults.

How the code was checked

Every Go block in Tasks 1 to 7 was built in a scratch copy of this branch, and then rebuilt task by task in a second copy, with go vet and gofmt -l clean after each task. Both copies ended byte-identical.

  • go vet on linux and darwin for activator, host, appconfig and instancekey; go build for windows, darwin and linux/arm64. (GOOS=windows go vet of activator fails on an existing test's unix.Mkfifo, which this plan does not touch.)
  • The tests were run, targeted and niced: every new test, plus the activator Seed, Start, Prepare, Control, Schedule, Claim and LaunchEnvironment families, the host claim and env report tests, and the whole appconfig, moves, moves/archive and instancekey packages. All pass.
  • The red states were run too, for Tasks 3 and 4. Each test the plan says fails does fail. Doing that caught one test, the hibernation restore, that passed with no key at all; it now asserts a key was minted first.
  • O2' was compiled and run against stage B. A scratch copy of origin/region-ha-1b-next-build (1651ff7d) had internal/instancekey added and the O2' diff applied. go vet was clean and go test ./internal/hostrelocate/ passed, every existing pinner test included.
  • Only the O2' wiring sketch (relocationConfigKey) is uncompiled, because relocationRecord exists only in stage B's plan text.
  • The sandbox refuses unix sockets, so tests that start children or bind control sockets were run with the sandbox off.
  • Scratch copies are deleted.

The rastrillo ask

Needed. Rastrillo's control socket client calls only PUT and DELETE /schedule/{name}. Nothing on the agent's socket lets an app say which key it uses.

One line: when carlos.InstanceKey returns $CARLOS_INSTANCE_KEY (step 2), call PUT /instance-key/adoption once per process, with the key's SHA-256, bearer CARLOS_ADMIN_TOKEN, a short timeout and no effect on the result. The full text is the plan's "Ask for rastrillo #66" section.

Top concerns

  1. Claim boxes cannot record adoption yet. Claim launches strip CARLOS_CONTROL_SOCKET (claim_snapshot.go:116), so an app that first adopts the platform key on a claim box has no record, and its relocation refuses (stuck). Region HA runs only on claim boxes, so source (b) works there only for records written while the box was legacy, until claim launches get a control socket (O6, claim Plan 9 Group D).
  2. Source (b) waits on rastrillo. Apps on e3ea08d, which lacks the call, run fine but never relocate. Paul needs to post the ask and get a release before telling owners to adopt.
  3. Source (c) has a residual. An app that keeps its own instance.key, never published, and also has CARLOS_INSTANCE_KEY in its configuration passes (c), though it seals with its own key. Requiring adoption records for configured keys would close it, at the cost of publishing a digest of a configuration secret. The plan follows the ruling and lists this as a follow-up.
  4. Stage B's O2 parser was wrong. It accepted export CARLOS_INSTANCE_KEY="k1", which the activator never delivers as the key. O2' parses with the activator's own parser, and its test pins the exported line as no key.
  5. Moves leave a stale adoption record on the source. The record is not in moves' journalled artifact lists, so a moved host's flat record stays behind. It vouches only for the same key, but an empty-installation census on that box would refuse it. The fix is a follow-up, with a journal version step.
  6. Ruling 18 treats fsync as not a host write. The claim early return fsyncs the env file outside the claim's guard. If the claim's owners disagree, that step moves under authorize, which re-reads the claim on every selection.
  7. A small cost on every launch. Every launch now does an open, an fchmod and two fsyncs on <host>.env. Rev 1 did this only on the mint.