Let a deployment name its one real Conduit, so the verdict log can populate - #759
Merged
Merged
Conversation
…pulate
CORA's claim is that the record is the artifact. Its authorization decisions
were not in that record: no Conduit stream existed anywhere, every one of the
~180 handler call sites passed the nil sentinel as conduit_id, and
TrustAuthorize._emit_verdict silently skips whenever the target Conduit
doesn't exist or has no open verdict logbook. Watch item 6 of
project_authorization_envelope_design.md named the fix precisely -- conduit
injection plus a seeded verdict logbook, not just flipping trust_policy_id --
with the trigger "a compliance/ops need for one queryable cross-layer log".
The JSR paper's D5 exhibit is that need.
The honest fix and the cheap fix turned out to be the same fix. Sweeping
conduit_id through ~180 call sites the way surface_id was swept would add a
real parameter with only one value until multi-zone topology exists, which
project_conduit_injection_design.md's WI10 defers on purpose. Resolving an
UNSPECIFIED (nil-sentinel) conduit_id to the deployment's one configured
Conduit is not a shortcut around that: NIL_SENTINEL_ID is already documented
as meaning "unspecified", never "none", so filling it in with a configured
default is exactly what the sentinel is for. A caller that passes a real,
non-nil conduit_id of its own is never overridden.
A forward-only migration seeds SYSTEM_LOCAL_ZONE_ID and SYSTEM_LOCAL_CONDUIT_ID
with an open verdict logbook, named deployment-neutrally since this migration
ships everywhere, not just 2-BM. Settings.trust_conduit_id resolves the
unspecified case in TrustAuthorize._effective_conduit_id, called once per
authorize() call so the SAME resolved value feeds both the Policy evaluation
and the Verdict write -- the row that gets written can never describe a
conduit the command was not actually evaluated against.
Three misconfigurations get a boot guard rather than a silent gap, because
this area keeps re-learning that asking for a control and not getting one is
the failure that matters:
- trust_conduit_id set with no trust_policy_id: AllowAllAuthorize is never
constructed with a conduit_id, so the setting would be read by nothing.
- trust_conduit_id set but the Conduit is missing or has no open verdict
logbook: looks wired, populates nothing.
- trust_conduit_id and trust_policy_id both set but the policy governs a
different conduit: Policy.evaluate checks conduit before surface or
principal, so this would deny EVERY command, not narrow what the policy
permits.
warn_if_verdict_log_dormant now checks the EFFECTIVE conduit rather than
always asking about nil, so a deployment that adopts this correctly gets no
false alarm, and one that adopts it incorrectly still gets a true one.
Default is unset for all three settings/guards, so every existing deployment
and roughly 2400 AllowAll-based tests are unaffected. 2-BM's own migration to
the real Conduit is deliberately not part of this change -- a separate step,
so the shadow period already accumulating there isn't interrupted by another
restart.
Verified against a real Postgres testcontainer with the migration applied
fresh (not just unit-level mocks): the Zone and Conduit fold correctly, the
verdict logbook is open, and an authorize() call from a caller passing nil
lands a Verdict row under the configured conduit. Both new boot guards and the
conduit-resolution logic are mutation-verified in both directions.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This was referenced Aug 31, 2026
xmap
added a commit
that referenced
this pull request
Sep 1, 2026
… one (#761) Every in-process call (RunWitness, the capture readers, the inference recorder, promote_seeded_fleet) now carries SYSTEM_IN_PROCESS_SURFACE_ID per #760, but the deployment's one configured Policy is bound to the HTTP door, so every one of those calls strict-denies at the surface check. Reading 2-BM's shadow log confirmed it: 99.998% of a 152,000-event window was exactly this mismatch, drowning out anything a real front-door near-miss would show. Gives the in-process door its own second, optional rulebook instead of widening Policy itself (Policy.surface_id stays a scalar; that general multi-surface case is deferred until MCP traffic exists to validate it against). TrustAuthorize resolves which configured policy governs a call from its surface_id, mirroring the trust_conduit_id shape from #759: Settings.trust_in_process_policy_id, default None, existing deployments and tests unaffected. Two new boot guards close the same "looks wired, governs nothing" gap the conduit knob already guards against: a backdoor policy set without a front one, and a backdoor policy that doesn't actually govern the in-process surface (or, once trust_conduit_id is set, the right conduit). promote_seeded_fleet now always promotes through AllowAllAuthorize rather than the caller's kernel.authz, matching pilot_seed.py's own kernel construction -- it is a rare, explicitly-operator-run bulk recovery outside every request surface, and should not need enrollment in whichever policy ends up governing the in-process door to run at all. Co-authored-by: xmap <16776958+xmap@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the gap the last PR (#757) documented but didn't fix: CORA's authorization decisions were not in the record CORA claims is the artifact.
The gap
TrustAuthorize._emit_verdictwrites oneVerdictrow per decision, but only when the target Conduit exists and has an open verdict logbook. On 2-BM: zero Conduit streams exist, and all ~180 handler call sites passconduit_id=NIL_SENTINEL_ID, so every write is silently skipped.warn_if_verdict_log_dormantexists solely to say this out loud at boot.This was a deliberate, named deferral. Watch item 6 of
project_authorization_envelope_design.mdstates the fix precisely: "conduit injection + a seeded verdict logbook, NOT just flippingtrust_policy_id", trigger "a compliance/ops need for one queryable cross-layer log." The JSR paper's D5 exhibit is that need.The choice: resolve, don't sweep
Two ways to close this. Thread
conduit_idthrough the ~180 call sites the waysurface_idwas swept — a real parameter change for a value that's identical everywhere until multi-zone topology exists, whichproject_conduit_injection_design.md's WI10 defers on purpose. Or resolve the deployment's one configured Conduit once, in the adapter.The second is not a shortcut around honesty; it's what the sentinel is for.
NIL_SENTINEL_IDis documented as meaning "unspecified," never "none" — so resolving it to a configured default is a default for the unspecified case, not a fabrication. A caller that passes a real, non-nilconduit_idof its own is never overridden. The precedent is exact:get_surface_idis itself a static return that ignores the request.What ships
A forward-only migration seeds
SYSTEM_LOCAL_ZONE_ID/SYSTEM_LOCAL_CONDUIT_IDwith an open verdict logbook, named deployment-neutrally ("System Local," not "2-BM") since it ships to every deployment.LogbookSchemamatches_TRAVERSALS_SCHEMAbyte-for-byte, so a hand-seeded Conduit's logbook declaration is indistinguishable from one opened through the API.Settings.trust_conduit_idresolves the unspecified case inTrustAuthorize._effective_conduit_id, called exactly once perauthorize()call so the same resolved value feeds both the Policy evaluation and the Verdict write:The row can never describe a conduit the command wasn't actually evaluated against.
Three boot guards, because this codebase keeps re-learning that asking for a control and silently not getting one is the failure that matters:
trust_conduit_idset with notrust_policy_id→AllowAllAuthorizeis never constructed with a conduit_id, so the setting would be read by nothing.trust_conduit_idset but the Conduit is missing or has no open verdict logbook → looks wired, populates nothing.trust_conduit_idandtrust_policy_idboth set but the policy governs a different conduit →Policy.evaluatechecks conduit before surface or principal, so this would deny every command, not narrow what's permitted. This is what makes leaving 2-BM's nil-bound policies unmigrated safe rather than an oversight.warn_if_verdict_log_dormantnow checks the effective conduit instead of hardcoding nil, so a correctly-adopting deployment gets no false alarm and a misconfigured one still gets a true one.What's deliberately not here
SurfaceKindwork (agent-raised commands are still ungovernable) — next PR.Verification
Unit + architecture: 49,100 passed. Contract: 3,436 passed. Full integration tier: 1,364 passed, including a new test file that applies the migration against a real Postgres testcontainer (not just unit-level folding) and confirms the Zone/Conduit fold correctly with an open logbook, plus a case driving
TrustAuthorizeend-to-end through the configured conduit with the caller passing nil. Both boot guards and the conduit-resolution logic are mutation-verified in both directions. Default is unset for every new setting, so every existing deployment and ~2400 AllowAll-based tests are unaffected.🤖 Generated with Claude Code