feat(access): durable forensic/raw MCP-access audit sink + tier-aware "Access" dashboard tab (JEF-490)#264
Merged
thejefflarson merged 1 commit intoJul 23, 2026
Conversation
… "Access" dashboard tab (JEF-490) Consumes the JEF-488 AuditSink seam. Two halves: HALF A — a DURABLE `AccessAuditSink` (a NEW dedicated sink, distinct from the DecisionJournal): an append-only, size-rotated subject·entry·tool·tier·time record on its OWN file on the journal PVC (`PROTECTOR_MCP_AUDIT_PATH`) plus a bounded in-memory ring replayed on boot. Reuses the decision journal's append/rotate/graceful-degrade PATTERN; unset/unwritable ⇒ in-memory only (no crash). Wired in run_loop as the sink the MCP server uses, shared with the dashboard. Only forensic/raw disclosures are recorded (the dispatch `is_disclosure` gate is unchanged). A new read-only GET `/api/access.json` (no-store, inherits the router-wide OIDC gate — 401 unauthenticated) exposes the caller's own tier + the audit rows REDACTED TO THE CALLER'S OWN TIER: the target-class of a raw pull is shown only to a forensic+ viewer, else the verbatim JEF-488 workload-identity sentinel. Reuses the render.rs sentinel and the redact tier ladder — one vocabulary across tool + screen. HALF B — a sixth "Access" Preact tab: Section 1 "your access" (a tier chip — word+glyph+colour, glyph aria-hidden, raw = the loud posture colour — over a cov-rows list of what each tier reveals/withholds); Section 2 "forensic & raw pulls" (a real semantic <table>, newest-first, raw rows carry the loud keyline, identity/target are auto-escaped JSX children). The empty state honestly distinguishes least-privilege-working from the log-reset fact (the "resets on restart" sub-line shows only when the sink is in-memory). Tests: durable round-trip + in-memory + unwritable-degrade for the sink; dispatch records exactly one line for a raw pull and none for redacted; view_model tier-aware redaction (redacted viewer sees the sentinel, forensic+ sees the target); HTTP 401/no-store + end-to-end tier redaction via signed tokens; serialize contract for the wire tags; Preact tests for the chip, table, escaping, and empty states. Split run_loop's test block into run_loop/tests.rs to stay under the 1,000-line cap. Also wires `PROTECTOR_MCP_AUDIT_PATH` onto the existing journal PVC in the protector chart (durable-on-PVC by default). Closes JEF-490 Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP
thejefflarson
enabled auto-merge (squash)
July 23, 2026 06:38
thejefflarson
deleted the
thejefflarson/jef-490-forensicraw-mcp-access-audit-sink-access-dashboard-tab-tier
branch
July 23, 2026 06:41
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.
Sprint finale for JEF-490 — consumes the JEF-488
AuditSinkseam. Closes JEF-490.HALF A — the durable audit sink (Rust)
mcp::AccessAuditSink(NOT the DecisionJournal — a disclosure audit is a distinct concern): an append-only, size-rotatedsubject·entry·tool·tier·timerecord on its OWN file on the journal PVC (PROTECTOR_MCP_AUDIT_PATH), plus a bounded in-memory ring replayed on boot. Reuses the decision journal's append/rotate/graceful-degrade pattern; unset/unwritable volume ⇒ in-memory only (never crashes).run_loopas the sink the MCP server uses (replacing the defaultTracingAuditSink), the SAMEArcshared with the dashboard. Only forensic/raw disclosures are recorded — the dispatchis_disclosuregate is unchanged.GET /api/access.json:no-store, GET-only, inherits the router-wide OIDC enforce layer (401 unauthenticated — no second gate). It is tier-aware: the audit rows are redacted to the CALLER's own tier — a redacted-tier viewer seesraw pull of [redacted — workload identity; forensic tier required] by alice@…; a forensic+ viewer sees the target. Reuses the JEF-488 sentinel string verbatim (render::S_ENTRY) and the redact tier ladder — one vocabulary across tool + screen.HALF B — the "Access" Preact tab
Access,engine/web/src/access/view.jsx.aria-hidden+ colour; raw = the loud posture colour) over acov-rowslist of what each tier reveals/withholds.<table>with<th scope>headers, newest-first, DOM order = visual order; raw-pull rows carry the loud keyline; all identity/target strings are auto-escaped JSX children.Durability
The sink is durable-on-PVC by default: the chart wires
PROTECTOR_MCP_AUDIT_PATHonto the existing journal PVC (a distinct file, never overloaded onto the decision journal). It degrades to in-memory when the volume is absent/unwritable, and the Access tab reports that honestly.Tests
cargo fmt;cargo clippy --all-targets -- -D warningsclean; fullcargo testgreen (894 lib + integration, incl. the 1,000-line file-size guard —run_loop's test block was split intorun_loop/tests.rs);npm run build && npm testgreen (99 tests);helm lintclean.Scope notes / decisions
preactTabsis mis-scoped: JEF-398 removed the per-tab flag — every tab is client-rendered automatically, so there is noengine.dashboard.preactTabskey in this chart to update. The Access tab ships with the bundle unconditionally. The cluster-repo follow-up is moot for the same reason (and../clusterwas not touched).aria-hiddenglyph, semantic table headers, escaping, global focus ring); did not stand up the tooling (out of scope; noted as a follow-up).🤖 Generated with Claude Code
https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP