Track B Phase 2 frontend — the #grants surface (read · simulate · revoke)
2026-06-21
LOCKSTEP TRACEABILITY MATRIX --- api_endpoints: ["consumes /auth/grants* — shipped in trackb-phase2-grants-backend"] sdk_methods_updated: ["none — dashboard JWT client (authFetch), not the apiKey SDK"] mcp_tools_updated: ["none"] ---
What We Built
The #grants page now ships in the dashboard's AUTHORIZATION group, consuming the Phase 2 backend (/auth/grants*) over the shared JWT authFetch client. It is read · simulate · revoke by design — humans observe authority, never mint it.
- List + "Who can…" capability-inverse (R3). A filter row drives subject/status filters and the capability-inverse query: type
tool:shelland the grid shows exactly the grants that authorize it, with a "computed server-side over effective scope" note — the same exact-match algebra enforcement uses, never a fabricated client glob. - Per-grant detail. Expanding a row loads the verified-chain summary (valid / cause, effective models + tools, and an integrity warning if the relational subject has drifted from the signed envelope), plus an inline signed
/checksimulator — enter a principal + requested models/tools and get the same signed allow/deny verdict the PDP renders, footed with the signingkid. - Break-glass revoke. Active grants carry a
[Revoke]action gated behind a blast-radius confirm ("cascades to the entire delegation subtree … committed to the tamper-evident chain … cannot be silently undone"). The server enforces owner/admin; the UI states the consequence. - Honest empty/unavailable states (R11). No grants ⇒ an issue-your-first-grant SDK snippet; capability absent ⇒ onboarding, never a broken grid. The nav item is reachable now that the backend is live; per-tenant availability is reflected at the page level.
Why It Matters
Phase 1 gave operators an honest _posture_; Phase 2 gives them _hands_. An operator can now answer "who can run a shell?" in one query, prove a specific grant resolves (or see exactly why it doesn't), simulate a decision before trusting it, and — if something is wrong — pull authority across an entire delegation subtree with a single audited action. That is the "stop it, and prove it" promise made operable.
How It Works
api/grants.ts wraps the five /auth/grants* routes in typed helpers over authFetch (the JWT path, not the apiKey SDK — the dashboard authenticates as a human). grantsAvailable() is a fail-hidden capability probe. pages/grants.ts renders with the dashboard's standard el/dashCard/confirmAction/showToast idioms; the route is registered in PAGE_IDS + PAGE_RENDERERS and the nav item in getMainNavGroups().
The Numbers
- 1 dashboard route (
#grants); 7 new client tests (capability-inverse query, check/revoke bodies, capability-detection true/404/network). - Dashboard build green (141 modules); 389 dashboard vitest tests pass; root lint clean. The two Phase-1 assertions that locked grants _out_ of the router were flipped to assert it is now registered.
Competitive Edge
Every gateway has an "API keys" page. None has a surface where you ask "who can do X," watch a signed verdict render from the same engine that enforces it, and revoke a cryptographic delegation subtree with the action itself committed to a tamper-evident ledger. The dashboard is the evidence plane made tangible.
Lockstep Checklist
- [x] API Routes: consumes
/auth/grants*(shipped in the Phase 2 backend log); no new backend. - [x] TS/Python SDK: none — dashboard JWT client, not the apiKey SDK.
- [x] MCP Schemas: none.
- [x] Tests: 7 new client tests; Phase-1 router assertions updated; full dashboard suite green.
Remaining (Phase 2 polish + Phases 3–4)
Shipped the core read/simulate/revoke surface. Deferred refinements: grant-chain-tree.ts visual tree (W2.3), client-side JWKS Ed25519 verification with server-attested fallback (W2.6 — today we render the server-attested summary, which R10 designates the safe default), revoke cascade animation, evidence-bundle download, and the API-keys grant-bound column (W2.8). Phase 3 (evidence honest) and Phase 4 (Plans) remain.