You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
serve: let a plugin supply the ICryptoProvider for secret fields; today serve installs LocalCryptoProvider after every plugin hook, so a KMS-backed provider (cloud#3573) cannot be mounted #30948
Filing gate: ④ coordination, the platform half of objectstack-ai/cloud#3573 (p1). That card carries ③ a task on the maintainer's word: 「同意立卡」, then 「相关任务都可以是 p1」 (2026-11-06).
Business reason. Enterprise security questionnaires ask for customer-managed keys. cloud#3573 has built KMS-backed ICryptoProvider implementations (AWS KMS, Azure Key Vault, Vault transit), branch claude/issue-3573-kms-crypto-providers on cloud. But no self-hosted objectos-ee boot can mount them, because objectstack serve decides the provider itself, after every plugin hook.
Reader who acts. The lane that owns packages/cli (triage sets lane and grade; cloud suggests p1, inherited from cloud#3573). Then the cloud seat wires the provider from a licence-gated security-enterprise plugin.
Dedupe (open and closed): setCryptoProvider and crypto provider seam searched on objectstack issues; no open card asks for a host-supplied provider.
Measured (objectstack main26c965cb; the same block is at cloud's pin c50e0dfa)
packages/cli/src/commands/serve.ts, the "Secret-field CryptoProvider wiring (host composition root)" block, runs after await runtime.start(), i.e. after kernel:ready / kernel:bootstrapped / kernel:listening.
When no sharedCryptoProvider exists, it builds a LocalCryptoProvider.
It then always calls dataEngine.setCryptoProvider(sharedCryptoProvider).
The datasource binder shares the same instance.
So whatever a plugin set during init is overwritten. The block's own comment promises the seam that does not exist: "production hosts swap this for a KMS/Vault-backed provider (e.g. via an env-gated branch or a dedicated plugin) before secrets are written."
Ask
serve consults a provider a plugin registered during init, under a kernel service name such as crypto-provider. It uses it for the data engine, the datasource binder and the default SettingsServicePlugin, and falls back to LocalCryptoProvider only when none is registered.
Runtime wiring only: no spec key and no stack-config knob, so there is nothing for an AI author to write.
The production refusal for an unstable local key stays as it is when the fallback is used.
Done when
A test plugin registering a provider during init sees secret-field writes sealed by it, and the binder uses the same instance.
Without a registration, behaviour is byte-identical to today.
cloud#3573 can then mount its provider from a plugin.
The platform half of objectstack-ai/cloud#3573 (customer-managed keys), which carries the maintainer's 「同意立卡」 and 「相关任务都可以是 p1」. Today objectstack serve builds LocalCryptoProvider after every plugin hook and always calls setCryptoProvider, so a provider that a plugin registers is overwritten. Triage re-read packages/cli/src/commands/serve.ts on main (the wiring block at about L5619–5630).
p1, inherited on the maintainer's word for cloud#3573's related tasks. Cloud-blocking. Lane domain:server (packages/cli; the retired domain:cli reads as domain:server). pm:queue.
Runtime wiring only: no spec key and no config knob, so it is not a protocol addition.
⚠️ For the implementing lane: whatever registers the provider holds every secret field's key. Two registrations should refuse at boot rather than letting the last one win. The production refusal for an unstable local key stays on the fallback path, as the card says.
Claim: PM loop patrol · 2026-11-06T14:29Z
Session: session_016WV4cytLmJxfAmgPVjUbbW
Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-30948-host-crypto-provider (placeholder; the card thread's harness-pinned branch replaces it by a full re-post of this claim before any push)
Worktree: objectstack-issue-30948
Domain: domain:server
Seat: domain:server#1
File surface (card Ask and Done-when, nothing else):
packages/cli/src/commands/serve.ts: the secret-field CryptoProvider wiring (about :5242-5345 and :5631-5647) consults a provider a plugin registered during init under one kernel service name, uses that one instance for the data engine, the datasource binder and the default SettingsServicePlugin, and falls back to LocalCryptoProvider only when none is registered. Two registrations refuse at boot (triage 6686459323).
Its tests under packages/cli/ (a test plugin that registers a provider during init; the no-registration path byte-identical).
One .changeset/ file.
⛔ packages/spec/** (stop and ask if the service name has to enter CoreServiceName or a contract), any stack-config knob or spec key, LocalCryptoProvider itself and the production refusal for an unstable local key (it stays on the fallback path), ui/**, docs/adr/**, skills/**, .claude/**, objectstack-ai/cloud. ⛔ No key material in a log line, error or test snapshot. Stop on breach and explain in the report
Container & model: M, mode:card-thread, model: default, effort: xhigh (p1, the holder of every secret field's key)
Clause-②: yes, expected (widening: a host may now supply the provider; nothing changes without a registration). The dev re-derives it from the diff
Metadata-protocol: none
Responsibility: who supplies the secret-field crypto provider on objectstack serve | none (runtime wiring) | the data engine, the datasource binder, SettingsServicePlugin, and objectstack-ai/cloud#3573's KMS providers
Thread-read: 6686459323
Premise: as the filer and triage measured: serve.ts builds LocalCryptoProvider after runtime.start() when none exists and always calls dataEngine.setCryptoProvider, so a provider a plugin set during init is overwritten (read on 6ddf827236 at about :5631-5647). The dev re-reads it on current main
Owed before landing: a CONTRACT_REVIEW_TIER review (the key-holder boundary)
Serial constraints cleared: an open-PR search for the crypto provider and setCryptoProvider finds no PR on serve.ts; the dev rebases on a collision
Claim: PM loop patrol · 2026-11-06T14:40Z (full re-post of claim 6687208082: the card thread's branch, the service name, and one constant's home)
Session: session_016WV4cytLmJxfAmgPVjUbbW (the seat); dev = card thread session_016WV4czs7cUFAphQEUFSrcz
Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/project-thread-bimizv (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: objectstack-issue-30948
Domain: domain:server
Seat: domain:server#1
File surface (card Ask and Done-when, nothing else):
packages/cli/src/commands/serve.ts: the secret-field CryptoProvider wiring (about :5242-5345 and :5631-5647) consults a provider a plugin registered during init under the kernel service name crypto-provider, uses that one instance for the data engine, the datasource binder and the default SettingsServicePlugin, and falls back to LocalCryptoProvider only when none is registered. Two registrations refuse at boot (triage 6686459323): the kernel already refuses a second registerService('crypto-provider') (naming the service); serve also refuses before start, naming both plugins, when two plugins declare providesServices: ['crypto-provider'] (ADR-0116). Recording the registrant in packages/core/src/kernel.ts is out of scope.
The service-name constant CRYPTO_PROVIDER_SERVICE in packages/types beside its precedent DATASOURCE_SECRET_RESOLVER_SERVICE (one constant and its export), so objectstack-ai/cloud imports it instead of a literal. No spec entry: CoreServiceName is the dispatcher's slot vocabulary and host-published services do not enter it; the registered value's type is the existing ICryptoProvider contract, unchanged.
Its tests under packages/cli/ (a test plugin that registers a provider during init; the no-registration path byte-identical).
.changeset/ files for @objectstack/cli and @objectstack/types at the levels the diff earns.
⛔ packages/spec/** (stop and ask if the service name has to enter CoreServiceName or a contract), any stack-config knob or spec key, LocalCryptoProvider itself and the production refusal for an unstable local key (it stays on the fallback path), ui/**, docs/adr/**, skills/**, .claude/**, objectstack-ai/cloud. ⛔ No key material in a log line, error or test snapshot. Stop on breach and explain in the report
Container & model: M, mode:card-thread, model: default, effort: xhigh (p1, the holder of every secret field's key)
Clause-②: yes, expected (widening: a host may now supply the provider; nothing changes without a registration). The dev re-derives it from the diff
Metadata-protocol: none
Responsibility: who supplies the secret-field crypto provider on objectstack serve | none (runtime wiring) | the data engine, the datasource binder, SettingsServicePlugin, and objectstack-ai/cloud#3573's KMS providers
Thread-read: 6687208082
Premise: as the filer and triage measured: serve.ts builds LocalCryptoProvider after runtime.start() when none exists and always calls dataEngine.setCryptoProvider, so a provider a plugin set during init is overwritten (read on 6ddf827236 at about :5631-5647). The dev re-reads it on current main
Owed before landing: a CONTRACT_REVIEW_TIER review (the key-holder boundary)
Serial constraints cleared: an open-PR search for the crypto provider and setCryptoProvider finds no PR on serve.ts; the dev rebases on a collision
{
"issue": 30948,
"status": "done",
"branch": "claude/project-thread-bimizv",
"pr": "https://github.com/objectstack-ai/objectstack/pull/30993",
"head": "2ff85dadc556ebc7e2a8b68ebc051be4ff5d5778",
"session": "session_016WV4czs7cUFAphQEUFSrcz",
"premise_still_valid": true,
"summary": "Draft PR #30993 (Fixes #30948, Clause-②: yes (widening), Metadata-protocol: none). A plugin registers an ICryptoProvider as kernel service 'crypto-provider' (CRYPTO_PROVIDER_SERVICE, new in @objectstack/types beside DATASOURCE_SECRET_RESOLVER_SERVICE) in init() and declares it in providesServices. os serve builds ONE host provider (createHostCryptoProvider) that chooses at kernel:ready, after every init: the registered provider, else the deferred LocalCryptoProvider it already built; that one instance goes to the data engine, the datasource secret binder and the default SettingsServicePlugin (its existing cryptoProvider option). Refusals at boot, none printing a registered value: two plugins declaring the service (named, before start; an undeclared second registerService is the kernel's own refusal), a registration after init, a replaced provider, a non-provider value (missing methods named), and any seal/open while plugins are still initializing. No-registration path: same key source and the same production refusal at kernel:ready; per the seat's ruling the default settings service now shares the one local instance (stated in the PR body, pinned by a test both ways). Premise re-measured on main 256a4b72 with a real os dev boot (plugin provider sealed scopes []); same probe on the branch sealed [object_secret_field].",
"tests": "head 2ff85dadc5: vitest serve-host-crypto-provider.test.ts (new, 16, integration tier) + serve-audit-registration.contract.test.ts + test/vitest-tiers-partition.test.ts: Test Files 3 passed (3), Tests 42 passed (42). cli unit tier at b308ea87 (one commit earlier): Test Files 1 failed | 392 passed (393), Tests 1 failed | 5527 passed (5528); the red was the #9863 audit-registration scan (anchor text 'for (const plugin of plugins)' appeared twice), fixed in 2ff85dad and that file passes above. types typecheck exit 0; cli typecheck exit 0 incl. check:test-typecheck OK (ledger unchanged) under NODE_OPTIONS=--max-old-space-size=6144. Ablation at head 2ff85dad via scripts/ablation-replace.mjs (test imports serve.ts from source, no dist): anchor 'read: () => ctx.getServices().get(CRYPTO_PROVIDER_SERVICE),' x1 -> x0, replacement 'read: () => undefined,' x0 -> x1, blob 4e50bae4d07d -> fb89a43cd489; first test FAIL 'the local fallback was built although a provider is registered' (1 failed | 15 skipped); restored blob == HEAD, git diff HEAD empty. Real boots (os dev --fresh, probe app outside the repo): main 256a4b72 'scopes sealed by the plugin provider: []'; branch '[\"object_secret_field\"]' with log line 'the crypto provider a plugin registered as crypto-provider wired'; branch without registration keeps 'LocalCryptoProvider wired (dev)'.",
"gates": "head 2ff85dadc5: dispatch-gates --ran reconciliation 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN, all 62 exit 0 (record /mnt/project-files/server-lane-seat1/30948/gates-2ff85dadc5.txt); pnpm lint exit 0. Repaired on the way: check:where-matcher (new double read a $-key as a field; now refuses). i18n-coverage, dual-build-cjs-loads, dts-closure exit 3/1 before a whole-tree build (missing dist / d.ts), exit 0 after it. CI check-runs not read: the PR was just opened.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"line_budget": "5 files, +933 / -60 (993 changed lines) vs the human-merge threshold 3000: under",
"files_changed": [
".changeset/30948-host-crypto-provider.md",
"packages/cli/src/commands/serve-host-crypto-provider.test.ts",
"packages/cli/src/commands/serve.ts",
"packages/types/src/crypto-provider-service.ts",
"packages/types/src/index.ts"
],
"deviations": [
"No-registration path is not byte-identical in one respect, on the seat ruling: the default SettingsServicePlugin is handed the shared local provider instead of building its own new LocalCryptoProvider() from the same key source. Stated in the PR body; pinned by a test (a setting either one sealed opens under the other).",
"packages/types was added to the file surface by the seat (claim re-post 6687449788); changeset carries @objectstack/types: patch.",
"cli check:test-typecheck needed NODE_OPTIONS=--max-old-space-size=6144 locally (default 2 GB heap OOM); no code change.",
"The harness asked to request a review from the requester via GitHub MCP; not done (standing line: no reviewers, no MCP writes). The harness commit trailer with a model name was not used (model-free trailer per os-dev)."
],
"mcp_calls": "4 — mcp__github__issue_read (get x1, get_comments x3), all reads; no write tool",
"api_writes": "2 relay strokes — pr_create (POST /repos/objectstack-ai/objectstack/pulls, POST /repos/objectstack-ai/objectstack/issues/30993/assignees), this comment (POST /repos/objectstack-ai/objectstack/issues/30948/comments); plus git push (not REST)",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"class: b · reach: named producer — the one-shot CLI secret paths (utils/one-shot-settings.ts, os secret rewrap) build their own LocalCryptoProvider and never consult a registered provider, so on a KMS-keyed deployment they cannot open what serve sealed · evidence: packages/cli/src/utils/one-shot-settings.ts:105 and :111 (new LocalCryptoProvider / deferredLocalCryptoProvider), commands/secret/rewrap.ts:63 (its provider is LocalCryptoProvider) · dedupe words: one-shot secret commands registered crypto provider",
"carrier: the seat / cloud#3573 · noted, not filed",
"class: c · reach: named producer — plugins mounted by registerInstalledPlugins are outside the declaration check (the kernel still refuses a second registration, without names); an app-composed SettingsServicePlugin is not handed the host provider; a plugin declaring the service but never registering it falls back to local silently · evidence: serve.ts refuseTwoCryptoProviderPlugins inputs (stack + resolver-mounted only) · dedupe words: installed plugins crypto-provider declaration",
"carrier: PR #30993 Acceptance notes · noted, not filed"
],
"owed_before_landing": "CONTRACT_REVIEW_TIER review (the key-holder boundary), per claim 6687449788"
}
Landing · 2026-11-06T18:25Z · PR #30993 MERGED 2026-11-06T18:02Z as 293a85b8 (Fixes #30948), landed by this seat on domain:server#1's hand-over (6690664327 on mailbox #30985); re-queued once after an unrelated queue ejection (#31012). The card is done; the seat drops pm:dispatched. Follow-up #31007 stays in the queue on its own.
Activity
objectstack-fleet commentedon Nov 6, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · p1 ·
domain:server· 2026-11-06T13:54Z⛔ Not a claim.
objectstack servebuildsLocalCryptoProviderafter every plugin hook and always callssetCryptoProvider, so a provider that a plugin registers is overwritten. Triage re-readpackages/cli/src/commands/serve.tsonmain(the wiring block at about L5619–5630).domain:server(packages/cli; the retireddomain:clireads asdomain:server).pm:queue.Generated by Claude Code
objectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
Claim: PM loop patrol · 2026-11-06T14:29Z
Session:
session_016WV4cytLmJxfAmgPVjUbbWAccount:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-30948-host-crypto-provider(placeholder; the card thread's harness-pinned branch replaces it by a full re-post of this claim before any push)Worktree:
objectstack-issue-30948Domain:
domain:serverSeat:
domain:server#1File surface (card Ask and Done-when, nothing else):
packages/cli/src/commands/serve.ts: the secret-field CryptoProvider wiring (about :5242-5345 and :5631-5647) consults a provider a plugin registered during init under one kernel service name, uses that one instance for the data engine, the datasource binder and the defaultSettingsServicePlugin, and falls back toLocalCryptoProvideronly when none is registered. Two registrations refuse at boot (triage 6686459323).packages/cli/(a test plugin that registers a provider during init; the no-registration path byte-identical)..changeset/file.⛔
packages/spec/**(stop and ask if the service name has to enterCoreServiceNameor a contract), any stack-config knob or spec key,LocalCryptoProvideritself and the production refusal for an unstable local key (it stays on the fallback path),ui/**,docs/adr/**,skills/**,.claude/**, objectstack-ai/cloud. ⛔ No key material in a log line, error or test snapshot. Stop on breach and explain in the reportContainer & model:
M,mode:card-thread,model: default,effort: xhigh(p1, the holder of every secret field's key)Clause-②: yes, expected (widening: a host may now supply the provider; nothing changes without a registration). The dev re-derives it from the diff
Metadata-protocol: none
Responsibility: who supplies the secret-field crypto provider on
objectstack serve| none (runtime wiring) | the data engine, the datasource binder,SettingsServicePlugin, and objectstack-ai/cloud#3573's KMS providersThread-read: 6686459323
Premise: as the filer and triage measured:
serve.tsbuildsLocalCryptoProviderafterruntime.start()when none exists and always callsdataEngine.setCryptoProvider, so a provider a plugin set during init is overwritten (read on6ddf827236at about :5631-5647). The dev re-reads it on currentmainOwed before landing: a
CONTRACT_REVIEW_TIERreview (the key-holder boundary)Serial constraints cleared: an open-PR search for the crypto provider and
setCryptoProviderfinds no PR onserve.ts; the dev rebases on a collisionobjectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
Claim: PM loop patrol · 2026-11-06T14:40Z (full re-post of claim 6687208082: the card thread's branch, the service name, and one constant's home)
Session:
session_016WV4cytLmJxfAmgPVjUbbW(the seat); dev = card threadsession_016WV4czs7cUFAphQEUFSrczAccount:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-bimizv(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree:
objectstack-issue-30948Domain:
domain:serverSeat:
domain:server#1File surface (card Ask and Done-when, nothing else):
packages/cli/src/commands/serve.ts: the secret-field CryptoProvider wiring (about :5242-5345 and :5631-5647) consults a provider a plugin registered during init under the kernel service namecrypto-provider, uses that one instance for the data engine, the datasource binder and the defaultSettingsServicePlugin, and falls back toLocalCryptoProvideronly when none is registered. Two registrations refuse at boot (triage 6686459323): the kernel already refuses a secondregisterService('crypto-provider')(naming the service); serve also refuses before start, naming both plugins, when two plugins declareprovidesServices: ['crypto-provider'](ADR-0116). Recording the registrant inpackages/core/src/kernel.tsis out of scope.CRYPTO_PROVIDER_SERVICEinpackages/typesbeside its precedentDATASOURCE_SECRET_RESOLVER_SERVICE(one constant and its export), so objectstack-ai/cloud imports it instead of a literal. No spec entry:CoreServiceNameis the dispatcher's slot vocabulary and host-published services do not enter it; the registered value's type is the existingICryptoProvidercontract, unchanged.packages/cli/(a test plugin that registers a provider during init; the no-registration path byte-identical)..changeset/files for@objectstack/cliand@objectstack/typesat the levels the diff earns.⛔
packages/spec/**(stop and ask if the service name has to enterCoreServiceNameor a contract), any stack-config knob or spec key,LocalCryptoProvideritself and the production refusal for an unstable local key (it stays on the fallback path),ui/**,docs/adr/**,skills/**,.claude/**, objectstack-ai/cloud. ⛔ No key material in a log line, error or test snapshot. Stop on breach and explain in the reportContainer & model:
M,mode:card-thread,model: default,effort: xhigh(p1, the holder of every secret field's key)Clause-②: yes, expected (widening: a host may now supply the provider; nothing changes without a registration). The dev re-derives it from the diff
Metadata-protocol: none
Responsibility: who supplies the secret-field crypto provider on
objectstack serve| none (runtime wiring) | the data engine, the datasource binder,SettingsServicePlugin, and objectstack-ai/cloud#3573's KMS providersThread-read: 6687208082
Premise: as the filer and triage measured:
serve.tsbuildsLocalCryptoProviderafterruntime.start()when none exists and always callsdataEngine.setCryptoProvider, so a provider a plugin set during init is overwritten (read on6ddf827236at about :5631-5647). The dev re-reads it on currentmainOwed before landing: a
CONTRACT_REVIEW_TIERreview (the key-holder boundary)Serial constraints cleared: an open-PR search for the crypto provider and
setCryptoProviderfinds no PR onserve.ts; the dev rebases on a collisionobjectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4czs7cUFAphQEUFSrcz
os-dev-report
{ "issue": 30948, "status": "done", "branch": "claude/project-thread-bimizv", "pr": "https://github.com/objectstack-ai/objectstack/pull/30993", "head": "2ff85dadc556ebc7e2a8b68ebc051be4ff5d5778", "session": "session_016WV4czs7cUFAphQEUFSrcz", "premise_still_valid": true, "summary": "Draft PR #30993 (Fixes #30948, Clause-②: yes (widening), Metadata-protocol: none). A plugin registers an ICryptoProvider as kernel service 'crypto-provider' (CRYPTO_PROVIDER_SERVICE, new in @objectstack/types beside DATASOURCE_SECRET_RESOLVER_SERVICE) in init() and declares it in providesServices. os serve builds ONE host provider (createHostCryptoProvider) that chooses at kernel:ready, after every init: the registered provider, else the deferred LocalCryptoProvider it already built; that one instance goes to the data engine, the datasource secret binder and the default SettingsServicePlugin (its existing cryptoProvider option). Refusals at boot, none printing a registered value: two plugins declaring the service (named, before start; an undeclared second registerService is the kernel's own refusal), a registration after init, a replaced provider, a non-provider value (missing methods named), and any seal/open while plugins are still initializing. No-registration path: same key source and the same production refusal at kernel:ready; per the seat's ruling the default settings service now shares the one local instance (stated in the PR body, pinned by a test both ways). Premise re-measured on main 256a4b72 with a real os dev boot (plugin provider sealed scopes []); same probe on the branch sealed [object_secret_field].", "tests": "head 2ff85dadc5: vitest serve-host-crypto-provider.test.ts (new, 16, integration tier) + serve-audit-registration.contract.test.ts + test/vitest-tiers-partition.test.ts: Test Files 3 passed (3), Tests 42 passed (42). cli unit tier at b308ea87 (one commit earlier): Test Files 1 failed | 392 passed (393), Tests 1 failed | 5527 passed (5528); the red was the #9863 audit-registration scan (anchor text 'for (const plugin of plugins)' appeared twice), fixed in 2ff85dad and that file passes above. types typecheck exit 0; cli typecheck exit 0 incl. check:test-typecheck OK (ledger unchanged) under NODE_OPTIONS=--max-old-space-size=6144. Ablation at head 2ff85dad via scripts/ablation-replace.mjs (test imports serve.ts from source, no dist): anchor 'read: () => ctx.getServices().get(CRYPTO_PROVIDER_SERVICE),' x1 -> x0, replacement 'read: () => undefined,' x0 -> x1, blob 4e50bae4d07d -> fb89a43cd489; first test FAIL 'the local fallback was built although a provider is registered' (1 failed | 15 skipped); restored blob == HEAD, git diff HEAD empty. Real boots (os dev --fresh, probe app outside the repo): main 256a4b72 'scopes sealed by the plugin provider: []'; branch '[\"object_secret_field\"]' with log line 'the crypto provider a plugin registered as crypto-provider wired'; branch without registration keeps 'LocalCryptoProvider wired (dev)'.", "gates": "head 2ff85dadc5: dispatch-gates --ran reconciliation 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN, all 62 exit 0 (record /mnt/project-files/server-lane-seat1/30948/gates-2ff85dadc5.txt); pnpm lint exit 0. Repaired on the way: check:where-matcher (new double read a $-key as a field; now refuses). i18n-coverage, dual-build-cjs-loads, dts-closure exit 3/1 before a whole-tree build (missing dist / d.ts), exit 0 after it. CI check-runs not read: the PR was just opened.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "line_budget": "5 files, +933 / -60 (993 changed lines) vs the human-merge threshold 3000: under", "files_changed": [ ".changeset/30948-host-crypto-provider.md", "packages/cli/src/commands/serve-host-crypto-provider.test.ts", "packages/cli/src/commands/serve.ts", "packages/types/src/crypto-provider-service.ts", "packages/types/src/index.ts" ], "deviations": [ "No-registration path is not byte-identical in one respect, on the seat ruling: the default SettingsServicePlugin is handed the shared local provider instead of building its own new LocalCryptoProvider() from the same key source. Stated in the PR body; pinned by a test (a setting either one sealed opens under the other).", "packages/types was added to the file surface by the seat (claim re-post 6687449788); changeset carries @objectstack/types: patch.", "cli check:test-typecheck needed NODE_OPTIONS=--max-old-space-size=6144 locally (default 2 GB heap OOM); no code change.", "The harness asked to request a review from the requester via GitHub MCP; not done (standing line: no reviewers, no MCP writes). The harness commit trailer with a model name was not used (model-free trailer per os-dev)." ], "mcp_calls": "4 — mcp__github__issue_read (get x1, get_comments x3), all reads; no write tool", "api_writes": "2 relay strokes — pr_create (POST /repos/objectstack-ai/objectstack/pulls, POST /repos/objectstack-ai/objectstack/issues/30993/assignees), this comment (POST /repos/objectstack-ai/objectstack/issues/30948/comments); plus git push (not REST)", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "class: b · reach: named producer — the one-shot CLI secret paths (utils/one-shot-settings.ts, os secret rewrap) build their own LocalCryptoProvider and never consult a registered provider, so on a KMS-keyed deployment they cannot open what serve sealed · evidence: packages/cli/src/utils/one-shot-settings.ts:105 and :111 (new LocalCryptoProvider / deferredLocalCryptoProvider), commands/secret/rewrap.ts:63 (its provider is LocalCryptoProvider) · dedupe words: one-shot secret commands registered crypto provider", "carrier: the seat / cloud#3573 · noted, not filed", "class: c · reach: named producer — plugins mounted by registerInstalledPlugins are outside the declaration check (the kernel still refuses a second registration, without names); an app-composed SettingsServicePlugin is not handed the host provider; a plugin declaring the service but never registering it falls back to local silently · evidence: serve.ts refuseTwoCryptoProviderPlugins inputs (stack + resolver-mounted only) · dedupe words: installed plugins crypto-provider declaration", "carrier: PR #30993 Acceptance notes · noted, not filed" ], "owed_before_landing": "CONTRACT_REVIEW_TIER review (the key-holder boundary), per claim 6687449788" }Generated by Claude Code
objectstack-fleet commentedon Nov 6, 2026
domain:server#2 · os-tesla · session_01L9qibyvxbrLP4jm4PargoH
Landing · 2026-11-06T18:25Z · PR #30993 MERGED 2026-11-06T18:02Z as
293a85b8(Fixes #30948), landed by this seat ondomain:server#1's hand-over (6690664327 on mailbox #30985); re-queued once after an unrelated queue ejection (#31012). The card is done; the seat dropspm:dispatched. Follow-up #31007 stays in the queue on its own.