Repository navigation
fix(cli): os serve refuses to boot when a plugins entry that may provide the crypto provider does not load - #31263
Conversation
…ide the crypto provider does not load An entry of the stack's plugins that does not load, and that nothing rules out as the crypto provider, now refuses the boot, naming the entry and why it did not load. An entry that never materialized says nothing about what it provides and is refused; one that declares the crypto provider in providesServices is refused; one that declares other services, or none, is printed and the boot goes on as before. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5mgePB9NQ4PNhvYVxk
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 35 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 867f8bb8a26fe5e434850ba0aaa0cd1e99daafad && git checkout 867f8bb8a26fe5e434850ba0aaa0cd1e99daafad
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 455229e7aebae2fe91ada77736377fe145defba3 5ed7975abfba52f8ad14d088902b79711e14ef4f && git checkout -B drift-repro 455229e7aebae2fe91ada77736377fe145defba3 && git merge --no-ff 5ed7975abfba52f8ad14d088902b79711e14ef4f
node scripts/docs-audit/affected-docs.mjs --json 455229e7aebae2fe91ada77736377fe145defba3 |
|
domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ Contract-review-claim: session_01FEkQ5o8PE7ZtVjcor51wBZ · head 5ed7975 · 2026-11-07T20:45Z |
|
domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ Contract reviewServed-tier: Inputs: the PR body, its one commit, the four-path net diff from merge-base ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentThe narrowing, read against the spec on Break attempts, each against the live tree: six shapes of an entry failing to load, or being dropped, between the stack loop and the provider construction. None defeats the check within this PR's scope; the detail is in the seat's private notes, under the disclosure ruling. Findings:
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ 席位意见:接受。 席位核对 — ACCEPT ·
|
Requested by Leehom · project thread
Refs #28284
Clause-②: no
Metadata-protocol: none
维护者速读
os serve启动时,若栈的plugins里某一项没能加载,且无法根据它的声明排除它是加密提供者(crypto-provider),则拒绝启动,并在报错里点名该项(plugins[i] ('<名称或包名>'))与加载失败原因。已加载出插件对象(或本身是对象)、但其providesServices未列出crypto-provider(包括没有providesServices)的项不受影响:照旧打印✗ Failed to load plugin后继续启动。@objectstack/cli(patch)。所有插件都能加载的启动不受影响。会被新拒绝的只有:包名无法导入的项,以及声明自己提供crypto-provider却加载失败的项。规则与一次性 secret 命令对同一 host 配置的规则一致。回滚即 revert 本 PR。packages/cli/src/commands/serve.ts的refuseUnloadedCryptoProviderPlugins,以及run()里栈插件循环之后、createHostCryptoProvider之前的那一次调用。pnpm --filter @objectstack/cli exec vitest run --project unit src/commands/serve-host-crypto-provider.test.ts,预期全部通过。What changes
os serverefuses to boot when apluginsentry that may provide the crypto provider does not load. The provider (crypto-provider) seals every secret field, datasource credential and encrypted setting, and the host chooses it once, so a boot without it does not choose another one. The check runs right after the stack's plugins are loaded and before the host's crypto provider is built, in every posture (plugins, anddevPluginsunder--dev). The refusal names each entry and the reason it did not load.providesServices: ['crypto-provider'](ADR-0116) is refused.providesServices, including one that declares noprovidesServices, is ruled out: it is printed with✗ Failed to load plugin, and the boot goes on without it, as before.refuseUnloadedCryptoProviderPlugins(and its input typeUnloadedStackPlugin) besiderefuseTwoCryptoProviderPluginsincommands/serve.ts. It throws the existingHostCryptoProviderRefusal.Nothing a stack can declare changes; a boot in which every entry loads is unchanged.
Why
QA 第三轮发现的安全加固(细节按披露裁决不公开)
Verification
Every reading below is on head
5ed7975ab(one commit onmainatd3f36861e) unless it says otherwise.Pins, red-first and restore
packages/cli/test/serve-crypto-provider-plugin-load-failure.integration.test.ts: four realos serveboots (src, through therunServehelper), one cause each. Two are refused and name the entry andcrypto-provider: an instance declaringprovidesServices: ['crypto-provider']that the kernel does not accept, and a package specifier that does not import. Two CONTROLs reach the ready banner on a bound port: an entry declaring another service that the kernel does not accept, and a stack whose every entry loads.packages/cli/src/commands/serve-host-crypto-provider.test.ts: five unit cases forrefuseUnloadedCryptoProviderPlugins(never materialized, materialized and declaring, an object entry, a ruled-out CONTROL, several named in one refusal), plus a source pin that the record sits in the plugin loop and the check sits between the loop andcreateHostCryptoProvider.serve.tsset back to itsmainblob, the integration file read 2 failed | 2 passed (4), the two refusal cases red and the two CONTROLs green. Restored: blob equal to HEAD,git diff HEADempty, 4 passed (4). The CLI runs fromsrchere, so no rebuild sits between mutation and run.Package suite, build and typecheck
pnpm turbo run build --filter=@objectstack/cli...: 63 tasks successful.tsc --noEmitinpackages/cli: exit 0.pnpm --filter @objectstack/cli testunder the verify lock: Test Files 605 passed | 1 skipped (606), Tests 7490 passed | 9 skipped (7499),VERDICT command-exit 0.Derived gates
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands: 64 families on this diff, every one run and exit 0 (check:type-check-debtunder the verify lock).--ranreconciliation: 64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN.Not measured:
os startwas not booted separately; it goes through the samerun().Human Minimum
packages/specholds no runtime logic. (Nopackages/specchange.)eslint --no-inline-configon the three changed source files: exit 0.main, one pull request.packages/spectouched: not touched.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code