carlos / platform Public

Instance key provision: rev 3 planner's report

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev b6fa2ed1fd5d5754994b485a2db83cbe21723ac2 parent 3947e1d
1 file changed, +54 −0
  • docs/superpowers/plans/INSTANCE-KEY-PLAN-REPORT-R3.md +54 −0

CI cancelled — superseded by a newer push — run details

diff --git a/docs/superpowers/plans/INSTANCE-KEY-PLAN-REPORT-R3.md b/docs/superpowers/plans/INSTANCE-KEY-PLAN-REPORT-R3.md
new file mode 100644
index 000000000..1eb5718d6
--- /dev/null
+++ b/docs/superpowers/plans/INSTANCE-KEY-PLAN-REPORT-R3.md
@@ -0,0 +1,54 @@
+# Instance key provision (L21): plan report, rev 3
+
+**Status:** rev 3 of the plan is written and committed. It answers Astra's round 2 (`CODEX-PLAN-INSTANCE-KEY-R2.md`) under Paul's rulings R-A to R-F, which are the plan's rulings 19 to 24. Nothing is pushed.
+- **Branch:** `instance-key-build`, worktree `/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-key-provision`
+- **Plan:** `docs/superpowers/plans/2026-10-10-instance-key-provision.md`, rev 3, commit 3947e1d9
+- **Tasks:** 7, then the final verification. Task 5 is now the adoption verb and its publication, and is mounted nowhere on main. Task 7 lost `ConfiguredInstanceKey`.
+
+## Dispositions (round 2)
+
+| # | Finding | Disposition |
+|---|---|---|
+| R2-1 | A configured key still bypasses the refusal (configured A, own B) | Accepted: source (c) dropped; configured keys need an adoption record too |
+| R2-2 | Configuration evidence neither identifies nor pins the key | Accepted: the record names the key by digest; the pin carries it; the install verifies it |
+| R2-3 | An adoption record outlives its writer | Accepted: the record binds host, incarnation, session and generation from the agent's launch state; every admission starts with none |
+| R2-4 | No publication on the running session; O6 not gated | Accepted: `hostfiles.PlatformRelay` onto the session's publisher; O6 a gate in O4 |
+| R2-5 | Moves leave the record behind | Accepted: O7 with the exact lists; O6 does not ship before O7 |
+| note | The record is tenant-forgeable | Answered by ruling 19 (trusted-helper assertion) |
+| R1-6 | The merge option | Closed: withdrawn by Astra |
+
+**Counts:** 5 accepted, 0 declined. One note answered, one round 1 finding closed.
+
+## What changed in the code
+
+- `instancekey`: the record is now version 2 and writer-bound (`Binding`, `ParseAdoption`, `Binds`). New: the sources (`SourcePlatform`, `SourceConfig`, `SourceOf`) and `VerifyDelivered` for the install.
+- `ClaimLaunchSnapshot` gains `InstanceKeySHA256` and `InstanceKeySource`, read from the same two files as the env; the identity is unchanged.
+- The adoption verb (`MountInstanceKeyAdoption`) takes its writer, key digest and source from an `AdoptionLaunch` that O6 builds per launch. It is no longer mounted on legacy launches.
+- New `hostfiles.PlatformRelay` stages a platform item on a running publisher without blocking the caller.
+- O2' for stage B is now (a) or (b'), with the pinned key in `PinObject.InstanceKey`. It replaces stage B's own rev 3 `KeyEvidence` reader.
+
+## How it was checked
+
+- Every Go block in Tasks 1 to 7 was built in a scratch copy, then rebuilt task by task in a second copy, with `go vet` and `gofmt -l` clean after each task. Both copies ended byte-identical. The plan's blocks were then extracted back out of the document and compared with the scratch files, byte for byte.
+- `go build` for windows, darwin and linux/arm64 of activator, host, hostfiles, instancekey and appconfig, and `go vet` on darwin: clean.
+- **Tests run**, niced:
+ - all new tests;
+ - the activator `Seed`, `Start`, `Prepare`, `Control`, `Schedule`, `Claim` and `LaunchEnvironment` families, with the sandbox off for sockets;
+ - the host claim and env report tests;
+ - all of `instancekey`, `hostfiles`, `moves` and `moves/archive`.
+
+ All passed. The full `go test ./internal/host/ -run Claim` also passed, in 1112 seconds. It exceeds go test's default 10-minute timeout on this shared box because of an existing 30,000-delivery test (`TestClaimCompletedProducersBoundStopAndRetry`, 1015 seconds), so the plan's sandbox note now says to pass `-timeout 30m`.
+- **O2' was compiled and run against stage B** at 919662d2, in a scratch copy with `internal/instancekey` added. `go build ./internal/...` and `go vet` were clean, and `go test ./internal/hostrelocate/` passed: 17 evidence cases, plus every existing pinner test.
+- Scratch copies are deleted. The shared `$TMPDIR/gocache` is untouched.
+
+## The rastrillo addendum
+
+The helper reports on every process start (at most once per process), and the body adds `"source": "platform" | "config"`, copied from a new agent-set `$CARLOS_INSTANCE_KEY_SOURCE`. It is in the plan's "Ask for rastrillo #66" section, ready to post.
+
+## Top concerns
+
+1. **The helper cannot know the source by itself.** A platform key and a configured key both arrive as `$CARLOS_INSTANCE_KEY`. The plan has the agent deliver `CARLOS_INSTANCE_KEY_SOURCE` beside it (agent-owned, part of O6) for the helper to echo, and the agent checks the echo against its own launch state. If Paul would rather not add a variable, the `source` field could be dropped from the request, because the agent already knows the source.
+2. **Nothing writes a record until O6 and O7 land.** The verb is built and tested but mounted nowhere: legacy launches have no writer session to bind to, and claim launches have no control socket. Platform-key and configured-key apps therefore do not relocate until claim Plan 9 Group D (O6) and the moves change (O7) ship, in that order. That is the explicit refusal of ruling 23.
+3. **A configured key's digest is published.** A `config` record puts the SHA-256 of a configured secret in the data bucket. That reveals nothing for a random key; a weak, hand-typed key could be guessed against it. It is listed as a follow-up.
+4. **Stage B has its own diverging O2' in plan text.** Its rev 3 (919662d2) has the `KeyEvidence` reader, with presence-only config evidence and the `export` parser. Stage B's planner must replace that step with this plan's O2' and rewrite its ruling 55, as O2' says.
+5. **One report lost to box loss inside the publisher's bound.** A box lost within that bound (a second by default) after the first adoption loses that report. The pin then refuses until a later session re-reports. That is safe, but worth knowing.