feat(security): PrincipalGuard — cross-principal attribution detector (Know Your Principal #898, Phase 1)#902
Merged
Merged
Conversation
… (Know Your Principal #898, Phase 1 brain) Pure-logic detector half of the ratified Know Your Principal standard: establish a topic operator ONLY from the authenticated sender (a content name can never become the operator — by type), detect operator-role-decision attributions in agent-authored text, and flag any credited to a non-bound/non-known principal (block for mandate/credential, warn for prose). 13 unit tests incl. the incident-replay regression (the real Caroline doc lines all caught; the same lines crediting the bound operator all pass). No runtime consumers yet — wiring into the topic-operator binding + session-start injection + review path are later increments. Spec: docs/specs/OPERATOR-IDENTITY-BINDING-SPEC.md (#897); incident CMT-1125.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…re docs-coverage class floor) The new PrincipalGuard core module tipped the docs-coverage class floor (54% < 55%). Fix = document the capability properly: a public 'Know Your Principal' concept doc on the docs site (the standard is ratified in the constitution) + an internal module reference. Both mention PrincipalGuard; coverage back to 55%.
JKHeadley
pushed a commit
that referenced
this pull request
Jun 6, 2026
…now Your Principal #898, increment 3) The READ side of the operator binding and the first runtime consumer of PrincipalGuard.evaluatePrincipalCoherence (#902). On the outbound delivery seam (checkOutboundMessage), every finalized agent message is checked against the topic's VERIFIED operator (TopicOperatorStore.asVerifiedOperator — the authenticated-sender binding, never a content name). When the message credits an operator-ROLE decision (approval / mandate / credential / lock / acting-for) to a principal who is NOT the bound operator — the "Caroline" identity-bleed failure, caught in the agent's OWN output — a structured finding is appended to state/principal-coherence.jsonl. SIGNAL-ONLY, modeled byte-for-byte on observeSelfViolation: fire-and-forget, fail-open, dark by default (monitoring.principalCoherence.enabled, absent=off). It NEVER blocks, delays, or rewrites the message; the recorded verdict is for analysis only and is never enforced. Observe-first so the detector's false-positive rate can be measured on real outbound before any warn/block surface is built. Tests (all three tiers, 14 total, green): - unit principal-coherence-operator-seam (5) — store->guard contract, both sides - integ principal-coherence-signal (5) — over the wire, gating both sides - e2e principal-coherence-lifecycle (4) — production boot, signal-only proof No new route/class/dependency. No migration needed (config read with ===true, absent=off; mirrors the sibling correctionLearning flag's convention). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
JKHeadley
added a commit
that referenced
this pull request
Jun 6, 2026
…now Your Principal #898, increment 3) (#910) The READ side of the operator binding and the first runtime consumer of PrincipalGuard.evaluatePrincipalCoherence (#902). On the outbound delivery seam (checkOutboundMessage), every finalized agent message is checked against the topic's VERIFIED operator (TopicOperatorStore.asVerifiedOperator — the authenticated-sender binding, never a content name). When the message credits an operator-ROLE decision (approval / mandate / credential / lock / acting-for) to a principal who is NOT the bound operator — the "Caroline" identity-bleed failure, caught in the agent's OWN output — a structured finding is appended to state/principal-coherence.jsonl. SIGNAL-ONLY, modeled byte-for-byte on observeSelfViolation: fire-and-forget, fail-open, dark by default (monitoring.principalCoherence.enabled, absent=off). It NEVER blocks, delays, or rewrites the message; the recorded verdict is for analysis only and is never enforced. Observe-first so the detector's false-positive rate can be measured on real outbound before any warn/block surface is built. Tests (all three tiers, 14 total, green): - unit principal-coherence-operator-seam (5) — store->guard contract, both sides - integ principal-coherence-signal (5) — over the wire, gating both sides - e2e principal-coherence-lifecycle (4) — production boot, signal-only proof No new route/class/dependency. No migration needed (config read with ===true, absent=off; mirrors the sibling correctionLearning flag's convention). Co-authored-by: Instar Agent (echo) <echo@instar.local> Co-authored-by: Claude Opus 4.8 <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.
What
The pure-logic detector brain of the "Know Your Principal" cross-principal coherence guard — the first implementation increment of the ratified standard (#898) and its spec (#897). Implements the §2 detector from
OPERATOR-IDENTITY-BINDING-SPEC.md.src/core/PrincipalGuard.ts:establishOperator(uid, name?)— a topic's operator is established only from the authenticated sender id; there is no code path that accepts a name from content (the Caroline failure mode is impossible by type).detectAttributions(text)— finds operator-role-decision shapes in agent-authored text:Mandate (X),X approved,locked with X,standing requirement from X,X dropped a token,on behalf of X.evaluatePrincipalCoherence(text, operator, knownUserNames)— flags any attribution to a principal who is neither the bound operator nor a known user; blocks authority/credential misattributions (mandate, credential), warns on prose.The incident-replay regression test (spec §test-plan)
The three real Caroline doc lines, with the topic bound to the operator, are all caught (block for the mandate + credential lines); and the same lines crediting the bound operator all pass (no false positives). Capitalized non-names ("Production approved", "The Board approved") are not flagged.
Scope
Pure logic, no runtime consumers yet — adding the file cannot change live behavior (see
upgrades/side-effects/principal-guard-phase1.md). The later increments wire it into the topic-operator binding, the session-start "who is my operator" injection, and the outbound/at-rest review path. 13 unit tests,tscclean.ELI16
A detector that reads the agent's own writing and flags it whenever the agent credits a decision to a person who isn't the verified operator and isn't a known user — the structural catch for the Caroline failure, where my session credited my operator's decisions to a different real person and nothing noticed. It establishes who the operator is only from the platform-verified sender (never a name in a document), and it ships with a replay test built from the actual incident's three document lines.
Incident: CMT-1125. Standard: #898. Spec: #897.
🤖 Generated with Claude Code