carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1# Instance key provision (L21): plan report
2
3**Status:** the plan is written and committed. Nothing is pushed.
4- **Branch:** `instance-key-provision`, worktree `/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-key-provision`
5- **Commit:** 151c4d94
6- **Plan:** `docs/superpowers/plans/2026-10-10-instance-key-provision.md`
7- **Tasks:** 5, then a final verification step.
8
9**How the code was checked.** Every code block in the plan was prototyped in a scratch worktree of 0d338c14 and passed these checks:
10- `go vet` on linux and darwin, for activator, host, moves, moves/archive and appconfig;
11- `go build` for windows and darwin, for activator and host;
12- `gofmt` clean.
13
14The tests were **not run**, because the shared-machine rules allowed only build and vet. The scratch worktree is deleted.
15
16## Rulings
17
181. **Where the key lives.** It goes in `<host>.env`, minted on the box. This is the "overlay" in `mergeEnv`'s sense: the file that already holds `CARLOS_ADMIN_TOKEN`. It is not appconfig's instance overlay (`ApplyInstance`, rendered to `<host>.config.env`). Minting in the console is rejected: that layer is app-editable, and it sits *under* `<host>.env` in `mergeEnv`. Stage B's L21 section pointed at the wrong layer.
192. **Format.** `base64.StdEncoding` of 32 `crypto/rand` bytes: 44 characters, padded. Every reader yields it verbatim.
203. **Who mints.** Every launch path mints the key right after the admin token, under `adminTokenMu`: `seedChild`, `spawnChildFrom`, nested `prepareAt`, and the claim's `EnsureClaimAdminToken`. The scheduler tick does not. Creation needs no new step: no instance process starts before `doWake`'s seed or start.
214. **Create-only.** A key of any shape is never rewritten. An empty value counts as absent. The key is appended, the file is `fchmod`ed to 0600, and the file and its directory are fsynced.
225. **Configuration wins.** If the rendered `<host>.config.env` already supplies `CARLOS_INSTANCE_KEY`, the platform mints none. Minting would change the app's effective key.
236. **Durability before the first start.**
24 - Legacy boxes have no publisher on main, and region HA can't run there. The key's durability is the admin token's: disk plus the move archive.
25 - On claim boxes, the launch source mints in `Select`, and `Materialize` calls C8's `Establish` before the app executes. C8 is bound (obligation O1) to publish the env bytes before returning.
26 - If the bucket is unreachable, the wake fails and the app does not start. The key stays on disk, and the retry publishes the same bytes.
277. **Backfill.**
28 - **Who:** the agent, at startup, on legacy boxes only. It runs after layout recovery and before the layout pass and `svc.Start`.
29 - **Which hosts:** ready activator instances only, and only where `<host>.env` already exists. It never creates the file, so a landing's empty path stays empty.
30 - **Idempotent:** it re-runs on every start.
31 - **Verifiable:** it logs one count line, which the rollout reads.
32 - Claim boxes mint under the guard at each cold admission instead.
33 - The "forced publish" is replaced by ruling 6: no process ever holds a key its generation's manifest lacks.
348. **Never overwrite.** There is a table with a test per path: create, backfill, existing key, claim retry, re-render, hibernation restore, move, relocation install (obligation O3), and layout move.
359. **Secrecy.** The key is never logged and never in an error. The file is 0600, and so is the launch file. The console never reads `<host>.env`, and `carlos env list` reads bucket config only. The moves env report uses HMAC only. Once published, the key sits in the bucket beside the database it seals.
3610. **Moves and relocation.** The key travels byte for byte in the sealed archive, and is placed only on an empty path. The env report flags a missing or different key. The moves probe never mints, because its pin would fail. Relocation install writes `EnvItem` verbatim (O3).
3711. **No switch.** An env var nobody reads is harmless. The claim launch identity changes once per host. Rollback is reverting the binary.
3812. **Scope.** Exec-backed activator instances only. Unit tenants, TCP-addr routes, sidecars and workers get no key, and `carlos.Running()` is false for them.
39
40## Obligations on other plans
41
42- **O1, the C8 provider:** `Establish` publishes `<host>.env`'s exact bytes before returning. An error fails the wake.
43- **O2, stage B pinner (ruling 25, `pin.go:250`):** `needKey` must also accept an `EnvItem` that carries `CARLOS_INSTANCE_KEY`. Otherwise every app that adopts the platform key goes `stuck` on every relocation, because it never writes `instance.key`.
44- **O3, stage B Task 8c:** add a test that relocation install keeps the published key.
45- **O4, stage B Task 10 item 9:** satisfied by this plan's rollout plus O1 and O2.
46
47## Open questions for Paul
48
491. **Replicas** (#162) mirror their home but get their own key. Should a replica get its home's key? That needs a way to copy the key between boxes.
502. **A key set in configuration.** Defer to it (as planned), or reserve the name? Reserving means refusing it in `carlos env set` / `secrets set` and stripping it at render, and the refusal message needs copy review.
513. **`report.token`.** The L21 candidate contract included it, but the decision covers the key only. Should the platform provision it too?
524. **Workers and sidecars.** Should they get their instance's key? Today they get none, and run as if off CARLOS.
535. **Restarts inside a claim session.** A restart inside a running session (not a cold admission) can mint outside the C8 publish point. This only affects instances admitted before this build. Accept it, or forbid minting outside the launch source on claim boxes?
54
55## Concerns
56
57- **The "always continuous" backup of `<host>.env` does not exist on main.** `hostfiles` is imported by nothing, `EnvItem` exists only on `region-ha-1b-next-build`, and no C8 provider exists, so claim serving wakes are refused. L21's durability on claim boxes depends entirely on O1, in a plan that isn't written yet.
58- **O2 is a real hazard.** Without it, adopting the helper and enabling region HA together leaves every adopted app with a nonempty database stuck.
59- **Copying a database between instances moves data, not keys.** The seeded-instance and run specs (#259-261), and any `db push` between instances, copy a database without its key. Those specs need to say what happens to sealed data.
60- **The key in the data bucket.** Region HA puts the key in the same bucket as the database it seals. That follows from the decision, but is worth stating.
61- **Existing tests may need small updates.** Activator or host tests that compare a child's whole environment, or an env file's exact bytes, after a start may now see one more line. Task 2 says to update such assertions without weakening them, and to list them in the commit.
62- **Line numbers for stage B.** Citations into stage B code refer to `region-ha-1b-next-build` at bb955916, not main.
63