Background
ContextCrawler's Tirith gate ([#33 / #32] context) blocks commands at the loopback hook layer, then writes downgrade events to `~/Library/Application Support/contextcrawler/downgrades.jsonl`. When the gate fires on a legitimate command, the escape hatch is `tirith trust add --scope repo --rule <rule_id>` from the shell.
The dashboard's Security pane (#171) shows every downgrade. It doesn't let you act on them.
Problem
A real operational flow looks like:
- Hoff runs `agy --add-dir ...` for peer review.
- Tirith gate flags `agy` as an editable / path / URL token because the prompt body includes `https://` examples.
- Block lands in the Security pane.
- Hoff has to flip to a shell, recall the rule_id, run `tirith trust add`, re-run the command.
The roundtrip breaks the dashboard's promise of "glanceable status" — every action requires context-switching back to terminal.
Proposed scope
First write-side feature on the dashboard
This is the first feature that mutates state from the loopback UI. Pattern to establish for later write features (#TBD #175 council annotations, etc.).
New endpoints
```
GET /api/security/trust → list current entries from tirith trust list --json
POST /api/security/trust → add an entry (body: {host, scope, rule_id})
DELETE /api/security/trust/ → remove
```
All three shell out to the `tirith trust` CLI — never poke Tirith's config files directly (config schema is upstream-owned).
Frontend behaviour
- New "Trust manager" sub-pane under Security
- Each recent downgrade row gets an inline [Trust this rule] button
- Click → modal showing the suggested `{host, scope, rule_id}` populated from the downgrade event
- Explicit confirm required — two-step interaction, not one-click (this is a security control we're loosening)
- Modal also shows the blast radius: "this will allow any command matching from in "
- Existing entries listed in a table with [Revoke] button per row
Loopback-only contract preserved
Server still binds 127.0.0.1 only. No CSRF protection needed (loopback + no shared cookies). But:
- Log every write action to `~/Library/Application Support/contextcrawler/trust_actions.jsonl` (timestamp, action, payload) — gives an audit trail mirroring the gate's own logs.
- Display the most recent 10 trust changes in the pane so the user always sees what was last modified.
Out of scope (v1)
- Editing rule definitions (only add/remove existing rules).
- Bulk operations.
- Trust suggestions / ML-driven recommendations.
- Anything that talks to a remote Tirith server.
Effort
M. ~1 week.
- 3 endpoints + tests (incl. CLI subprocess wrapping with proper error surfacing): 2 days
- Frontend pane + modal + confirm UX: 2 days
- Audit log + display: 1 day
- Doc + screenshot + peer-review pair: 1 day
Why now
Single-model pick (Claude only in 3-way brainstorm 2026-05-25), but the operational gap is real. Closes the loop between "I see the block in the dashboard" and "I can act on it without flipping back to shell". Becomes the pattern for all future write-side features.
Dependencies
Risk
Loosening the gate from the UI is a security-relevant action. The two-step confirmation + audit-log + blast-radius display are non-negotiable parts of v1; do not ship the endpoint without them.
Background
ContextCrawler's Tirith gate ([#33 / #32] context) blocks commands at the loopback hook layer, then writes downgrade events to `~/Library/Application Support/contextcrawler/downgrades.jsonl`. When the gate fires on a legitimate command, the escape hatch is `tirith trust add --scope repo --rule <rule_id>` from the shell.
The dashboard's Security pane (#171) shows every downgrade. It doesn't let you act on them.
Problem
A real operational flow looks like:
The roundtrip breaks the dashboard's promise of "glanceable status" — every action requires context-switching back to terminal.
Proposed scope
First write-side feature on the dashboard
This is the first feature that mutates state from the loopback UI. Pattern to establish for later write features (#TBD #175 council annotations, etc.).
New endpoints
```
GET /api/security/trust → list current entries from tirith trust list --json
POST /api/security/trust → add an entry (body: {host, scope, rule_id})
DELETE /api/security/trust/ → remove
```
All three shell out to the `tirith trust` CLI — never poke Tirith's config files directly (config schema is upstream-owned).
Frontend behaviour
Loopback-only contract preserved
Server still binds 127.0.0.1 only. No CSRF protection needed (loopback + no shared cookies). But:
Out of scope (v1)
Effort
M. ~1 week.
Why now
Single-model pick (Claude only in 3-way brainstorm 2026-05-25), but the operational gap is real. Closes the loop between "I see the block in the dashboard" and "I can act on it without flipping back to shell". Becomes the pattern for all future write-side features.
Dependencies
Risk
Loosening the gate from the UI is a security-relevant action. The two-step confirmation + audit-log + blast-radius display are non-negotiable parts of v1; do not ship the endpoint without them.