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")
- 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.
- Defaults visible, never hidden: every input shows the shipped default next to the current value. Drift is obvious.
- Suggestions from real data: don't ask the user to invent glob patterns from a blank field; pull them from blocks that already happened.
- Confirmation matches blast radius: tightening = one click; loosening = explicit modal with what-it-will-do.
- Undo via audit log: every write logged with previous value, one-click revert from the audit pane.
- 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.
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)
Translated:
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:
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:
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:
UX principles ("low gate to use")
Out of scope (v1)
Effort
M-L. ~1-1.5 weeks.
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:
Ship without any of these → reject the PR.