feat(dashboard): per-image signing inventory in the Admission view (JEF-262)#137
Merged
thejefflarson merged 1 commit intoJul 1, 2026
Conversation
…EF-262) Surface JEF-261's observed signing posture for every image as a dedicated signing-inventory section in the Admission tab, sitting between the tallies header and the decision log. Two hard operator rules: the posture is always signed / invalid signature / not signed (or the transient checking) — never n/a; and the "if enforced" column is always the binary would-admit / would-block (only a verifying signature would admit; fail-closed otherwise). - New presentation `SigningPosture` enum + `SignerProps`/`SigningRowProps`/ `SigningRepoProps` in view_model/props.rs (a presentation mirror; the domain `signature::posture::SigningPosture` is never imported into components). - New view_model/signing_inventory.rs: partitions the sweep's `Image/<ref>` observation rows out of the decision log, derives a short signer label + issuer badge from the (untrusted) Fulcio SAN, splits each image into repo + digest/tag, and groups images under their repo. Producer untouched. - Component: `invalid` is the loud channel (breach keyline), plain `not signed` is calm; each posture carries colour + glyph + word; the ref/signer truncate in-row with an ellipsis (never break-all), full value in the <details> panel + title=. Honest empty: "no images observed yet" (explicitly not an all-clear). - Removed the old signature *gate* column from the decision log (posture now lives in the inventory). Tallies are now derived from the webhook DECISION rows alone, so observation rows can't inflate admitted/audited/denied. - STYLEGUIDE: added the signing-posture enum to the colour-not-alone gate + component→token map. Light-theme tokens only; no raw hex; no inline style. - Seeded the dashboard_preview sample log with every posture. Component + view_model tests cover signed-with-identity, invalid, not-signed, checking, empty, repo grouping, ref splitting, the binary if-enforced, and an escaping test on the untrusted identity. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP
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 JEF-262
What
Surfaces JEF-261's observed signing posture for every image as a dedicated signing-inventory section in the Admission tab, between the tallies header and the decision log. It replaces the misleading gate vocabulary (
n/a+ "would admit" for everything ungated) with two hard operator rules:Changes (presentation only)
view_model/props.rs— new presentationSigningPostureenum +SignerProps/SigningRowProps/SigningRepoProps. Presentation mirror only; the domainsignature::posture::SigningPostureis never imported into components. Removed thesignaturegate field fromDecisionRowProps.view_model/signing_inventory.rs(new) — partitions the sweep'sImage/<ref>observation rows out of the decision log; derives a short signer label (GitHub Actions SAN →org/repo, email kept verbatim) + issuer badge (github actions/google/sigstore/ host) from the untrusted Fulcio SAN; splits each image into repo + digest/tag; groups images under their repo. The producer (JEF-261 sweep) is untouched.view_model/admission.rs— tallies are now derived from the webhook decision rows alone (summing the deduped counts == the log's own tallies), so pure observation rows no longer inflate admitted/audited/denied.build_admission_viewdrops its now-redundanttalliesparam.components/admission_view.rs— new signing-inventory section reusing thegate-chipglyph+word vocabulary, the<table>grid, and the<details>expand pattern.invalidis the loud channel (breach keyline); plainnot signedis calm. Each posture carries colour + glyph + word. The ref + signer line truncate in-row with a single-line ellipsis (neverbreak-all); the full ref/SAN/issuer live in the expand panel +title=. Honest empty: "no images observed yet" (explicitly not an all-clear). Removed the old signature gate column from the decision log.dashboard.css—.signingtable shares the.decisionsgrid;--sign-*chip tokens; ellipsis truncation. Light-theme tokens only; no raw hex; no inlinestyle=.docs/STYLEGUIDE.md— added the signing-posture enum to the colour-not-alone gate + component→token map.examples/dashboard_preview.rs— seeded the sample log with every posture so the section renders across scenarios.Decision recorded
record.would_admit): the sweep records observation rows with the defaultwould_admit = true; rendering that verbatim is exactly the misleading "green stamp" the ticket kills. The binary is derived from the posture in the view_model layer (signed → admit; invalid / not-signed / checking → block), which honors "adapt in your view_model layer, don't modify the producer."Image/<ref>sweep rows with decisionallowinto the same log, solog.tallies()counted them as admits. Recomputing the header counts from the decision rows in the view_model keeps the admitted/audited/denied honest without touching the producer.Tests
view_model/signing_inventory(new): signed-with-identity (org/repo + badge), email signer, no-issuer, invalid, not-signed, checking, unknown→checking, would-admit binary, repo grouping, digest/tag/port ref splitting, partitioning, empty, dedup count.Checks
cargo fmt --check·cargo build·cargo build --example dashboard_preview·cargo clippy --all-targets(clean) ·cargo test— 401 unit + 9 dashboard guards + file-size guard all green.🤖 Generated with Claude Code