fix(verify): os verify never creates key material in the key home; the harness seals under an in-process data key - #21507
Conversation
…y home (red first) The pin boots the harness in a development posture with an empty key home, with a key file already there, and with OS_SECRET_KEY set, and asserts no key material is created and no real key is the one in use. The default provider minting in the same posture and home is the control. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016GiHYRmLSNWTfbX9gVQkpz
…nting one in the key home bootStack composed the settings service with no cryptoProvider and bound the engine to a bare LocalCryptoProvider. In the development posture the harness forces, with no env key and no key file, both minted a key file in the key home; with a key on the host, both sealed fixtures under it. The harness now holds one LocalCryptoProvider over an explicit random key, created once per process and never persisted, and hands that instance to the settings service and to the engine. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016GiHYRmLSNWTfbX9gVQkpz
…ess data key Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016GiHYRmLSNWTfbX9gVQkpz
📓 Docs Drift CheckThis PR changes 1 package(s): ⛔ 1 release-owned page(s) name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 9e8db835a446205cceed04d9c721f3a5524e6b52 && git checkout 9e8db835a446205cceed04d9c721f3a5524e6b52
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2ee8383f4e16248322a45a3e4fde5de75598eef4 e8a091457cee686bf1cbabf1c7c4e8d36a7ef8ae && git checkout -B drift-repro 2ee8383f4e16248322a45a3e4fde5de75598eef4 && git merge --no-ff e8a091457cee686bf1cbabf1c7c4e8d36a7ef8ae
node scripts/docs-audit/affected-docs.mjs --json 2ee8383f4e16248322a45a3e4fde5de75598eef4
|
Fixes #21499
Clause-②: no
What changed
bootStack(packages/verify/src/harness.ts) composed the settings service with no crypto provider and bound the engine to a bareLocalCryptoProvider.bootStackforces a development posture, where both providers resolve a data key the way a server does: an env key, then the key file in the key home, and with neither they mint the key file. Soos verify, a one-shot command over an in-memory database, left key material in the key home. On a host that already had a key, the harness sealed its throwaway fixtures under that real key.The harness now holds ONE
LocalCryptoProviderover an explicit random key (harnessCryptoProvider, module-private):new SettingsServicePlugin({ cryptoProvider })) and the engine (setCryptoProvider) get the same instance, sosecretfields and encrypted settings still seal AND open on a keyless host. That is why the CLI's one-shot shape from PR fix(cli): a one-shot command never mints a data key in the key home (#21471) #21497 ("read an existing key, or refuse every call") does not fit here;databaseFile(the harness's restart) open each other's secrets, as a real host's stable key would let them.Shape choices, measured (dispatch zone 2)
6f17d1d364.harness.ts:498wasnew SettingsServicePlugin()with no provider, and:694wasengine.setCryptoProvider(new LocalCryptoProvider()).packages/cli/src/commands/verify.ts:195and:219callbootStack(config, { multiTenant })andbootStack(config, { multiTenant, security }).BootOptionshas no crypto option.bootStackin this repository passes or needs a key: every boot is an in-memory database or a caller-owned temp file. So the harness owns its provider internally.BootOptionsand every export are unchanged,Clause-②: noholds, andpackages/cli/src/commands/verify.tsis untouched.Evidence
Public door. Built CLI,
examples/app-todo,os verify --json, development posture, no env key, a fresh empty key home (OS_HOME):@objectstack/verifybuilt from6f17d1d364, rebuilt; dist preflight: marker absent)dev-crypto-keyBoth runs exit 1 on the same pre-existing fidelity gap on
todo_task.tags(see Acceptance notes). It is unrelated to this change.Pin
packages/verify/src/harness.key-custody.test.ts. Every boot runs in a hook, and the cases only assert.465c22036a), against the unfixed harness: 4 failed, 3 passed.dev-crypto-key.[true, true].OS_SECRET_KEY:[true, true].dev-crypto-key.generated-filein this posture and home.7c3a1843ce): 7 passed.scripts/ablation-replace.mjs:git diff HEADis empty;Unsupported state or unable to authenticate data(6 passed, 1 skipped, file red)../harness.jsfrom source, so nodist/leg applies to it.Local runs at HEAD
e8a091457c:pnpm --filter @objectstack/verify exec vitest run: 17 files, 127 tests passed.pnpm --filter @objectstack/verify typecheck: exit 0, tsc plus the test layer.--listFileson the test config holds the new test (17 of 17 test files).node scripts/pm/dispatch-gates.mjs --commands: 62 families derived and all 62 run. The--ranreconciliation reads 0 NOT-MEASURED and 0 UNRUN.check:dual-build-cjs-loadsfirst exited 3 (PREREQUISITE NOT MET: 9 packages had nodist/). After a cache-replay build of those 9 it exited 0.pnpm lintover the whole repository, unnarrowed: exit 0.Not run locally: CI's full suites, and the CLI integration tier (no
packages/clifile is touched).Serial note
PR #21497 adds an enumeration pin over
packages/cli/src. It had not landed when this PR was opened, and this branch sits on6f17d1d364. This diff touches nopackages/clifile, so that pin's population is unchanged. The seat mergesmainand re-runs that pin at landing.Acceptance notes
os verify --jsononexamples/app-todoexits 1 onmainwith one fidelity gap. The derived write puts the scalarimportantintotodo_task.tags, aselectwithmultiple: true, and reads back the array["important"]. It is reported to the seat as a finding and is not touched here.bootStackunder vitest also stops minting into the runner's key home. The harness forces a development posture, so the old default minted there too.Generated by Claude Code