Skip to content

feat(gain-web): supply-chain gate manager pane — UI-driven config + overrides (depends #162/#172) #177

Description

@thehoff

Background

ContextCrawler's supply-chain gate (#33, #141, #142, #146) reads its config from `~/.config/contextcrawler/supply-chain.toml`:

```toml
[npm]
cooldown_days = 3
block_severity = "HIGH"

[pypi]
cooldown_days = 3
block_severity = "HIGH"

[overrides]
always_allow = ["@types/*", "@scope/known-good"]
always_deny = ["left-pad"]
```

The dashboard's Security pane (#171) shows every blocked install. The Installs pane (#172) shows every install across every project. But to actually change the gate's behaviour — extend cooldowns, allowlist a package, raise the severity floor — the only path is editing the TOML by hand. New users have to read the gate docs end-to-end to know what knobs exist.

Problem (the Hoff's words, 2026-05-25)

"I'd like a webUI manager for the supply chain we created, make it easy to manage and a low gate to use not reading docs for days."

Translated:

  • The gate is opt-in and powerful, but the on-ramp is a docs-then-TOML slog.
  • A user gets blocked → has to leave the dashboard → reads docs to find the right override key → hand-edits TOML → wonders if they got the syntax right → reruns the command.
  • Operational friction kills adoption of a gate that's actually doing the right thing 99% of the time.

Proposed scope

Goal

A dashboard pane that lets the user manage the gate's full config without ever reading docs OR touching TOML. Doc text lives inline as field help; TOML is rendered, never edited by hand.

New pane: "Supply chain"

Sits next to the Security pane in the nav. Three sections:

A. Ecosystem defaults (per-ecosystem cards)

One card per supported ecosystem (npm / PyPI / cargo / RubyGems). Each card:

  • Cooldown days — number stepper with current value + default. Inline help: "Minimum days since publish before auto-allow. Defends against fresh-release malware (the most common npm attack pattern)."
  • Block severity — segmented control: Low / Medium / High / Critical. Inline help: "OSV findings at or above this severity block the install. Lower = more strict."
  • Reset to defaults button — single click reverts the card to ContextCrawler shipped defaults.

B. Overrides — allow / deny lists with suggestions

Split table: `always_allow` left, `always_deny` right. Each row: glob pattern + add date + last-fired counter + [Revoke] button.

Inline "Suggest from recent blocks" prompt:

"@earendil-works/pi-coding-agent has been blocked 3 times in the last 7 days. [Allow once] [Allow always] [Block always] [Ignore]"

Pulls from the install ledger (#172). One-click adds the right glob to the right list. Two-step confirmation for always_allow entries because the user is loosening a security control — modal shows the blast radius ("this allows every version of @earendil-works/pi-coding-agent from now on").

C. Audit log

Last 50 config changes from a new `~/Library/Application Support/contextcrawler/supply_chain_actions.jsonl` (mirrors the pattern in #176). Each row: timestamp, action (add allow / remove deny / cooldown changed / ...), payload, who initiated ("dashboard" / "cli" / "manual edit").

Diff preview before save

Every change shows a small inline diff (3-line context) of the underlying TOML before the save button activates. Reinforces the underlying file shape without forcing the user to learn it.

New endpoints

```
GET /api/supply-chain/config → current effective config (TOML parsed → JSON)
GET /api/supply-chain/defaults → ContextCrawler shipped defaults (for the "reset" button)
PUT /api/supply-chain/config → write new config (validates first; atomic rename)
GET /api/supply-chain/suggestions → "packages blocked N times in last 7d" list from install ledger
POST /api/supply-chain/overrides → add allow / deny entry (body: {kind: "allow"|"deny", glob})
DELETE /api/supply-chain/overrides/ → revoke
GET /api/supply-chain/actions → audit log tail
```

PUT writes via temp-file-then-rename to prevent partial writes corrupting the config on crash mid-save.

Loopback-only contract preserved

Same posture as #176:

  • Server still binds 127.0.0.1 only
  • All write actions logged to `supply_chain_actions.jsonl`
  • Two-step confirmation for always_allow additions (loosens security) — single-step for always_deny (tightens)
  • Schema-validated on the server: reject invalid severity, malformed glob, negative cooldown_days

UX principles ("low gate to use")

  1. Zero doc reading required: every field has inline help text. Severity field shows what each level means. Cooldown shows what "3 days" actually defends against.
  2. Defaults visible, never hidden: every input shows the shipped default next to the current value. Drift is obvious.
  3. Suggestions from real data: don't ask the user to invent glob patterns from a blank field; pull them from blocks that already happened.
  4. Confirmation matches blast radius: tightening = one click; loosening = explicit modal with what-it-will-do.
  5. Undo via audit log: every write logged with previous value, one-click revert from the audit pane.
  6. TOML visible but never primary: collapsible "View raw TOML" section so power-users can sanity-check what's being written, but the form is the workflow.

Out of scope (v1)

  • Editing the gate's regex / parsing rules — that's compiled-in policy, not user config.
  • Importing override lists from external sources (npm-scope subscriptions, GitHub Advisory feeds) — own issue.
  • Per-project overrides — current TOML is global. Per-project scoping is a bigger schema change; defer.
  • Bulk import / export of override lists — v2.

Effort

M-L. ~1-1.5 weeks.

  • TOML parse + write helpers with atomic-rename: 1 day
  • 7 endpoints + schema validation + tests: 2 days
  • Audit log + diff preview infra: 1 day
  • Frontend pane (form fields, override lists, suggestions, modal flows): 3 days
  • Inline help-text copy (Aus English, hyphens, no em-dashes): half day
  • Codex + agy peer review pair: 1 day

Why now

The Hoff explicitly asked for this 2026-05-25, framing it as a usability gap. Three-way brainstorm consensus 2026-05-25 already picked the cost overlay (#175) and Tirith trust manager (#176) as write-side UI siblings. This is the third pane in that family and the one with the strongest "reduce barrier to adopting an existing feature" payoff.

Per his TRAIT pattern: ContextCrawler is local-first and security-operational — but a security control nobody can configure adoption-friction-free is a security control that gets disabled. `CONTEXTCRAWLER_SUPPLY_CHAIN=off` is too easy.

Dependencies

Risk

Loosening the supply-chain gate from the UI is by definition a security-relevant action. Non-negotiable v1 requirements:

  • Two-step confirm for every `always_allow` addition
  • Audit log mandatory (no opt-out)
  • Blast-radius display in every loosening modal
  • Schema validation server-side — invalid configs never land on disk
  • Atomic write (temp file + rename) — partial-write must not corrupt the gate

Ship without any of these → reject the PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions