Track B Phase 2 gate — JWT-human grant read/simulate/revoke (the CR-grants block)

2026-06-21

capability-grantsscope-enforcementaudit-chaingovernance-dashboard

LOCKSTEP TRACEABILITY MATRIX --- api_endpoints: [ "GET /auth/grants", "GET /auth/grants/{id}", "GET /auth/grants/{id}/chain", "POST /auth/grants/{id}/check", "POST /auth/grants/{id}/revoke", ] sdk_methods_updated: ["none — dashboard-internal JWT mirror of existing /v1/grants SDK surface"] mcp_tools_updated: ["none — agent grant surface unchanged (br_grant_* already covers /v1)"] ---

What We Built

Track B (the governance dashboard) is a four-phase plan; Phase 1 shipped the honest posture/evidence surfaces and containment actions. Phase 2 was gated on a backend "CR-grants block" — a committed-or-cut set of JWT-human grant routes — before any dashboard grant UI could be built. This increment delivers that gate.

Five routes now exist under /auth/grants, the human mirrors of Increment 3's apiKey /v1/grants surface, all Supabase-JWT-authed and tenant-scoped:

  • GET /auth/grants?subject=&status=&root=&capability=&limit= — list, including the "Who can…" capability-inverse (R3): ?capability=tool:shell returns the grants that actually authorize that capability, computed server-side over each grant's effective scope using the same exact-match algebra (matchCapabilityScope) enforcement uses — never a fabricated client-side glob.
  • GET /auth/grants/{id} — record + a verified chain summary bound to the grant's signed-envelope subject (not the unsigned relational column).
  • GET /auth/grants/{id}/chain — the offline-verifiable bundle (root-first envelopes + signatures + JWKS ref).
  • POST /auth/grants/{id}/check — a signed PDP verdict via the same shared verdict core the apiKey route and the MCP br_grant_check adapter consume, so a dashboard simulation can never diverge from inline enforcement.
  • POST /auth/grants/{id}/revokebreak-glass, owner/admin only (R2).

Why It Matters

The product promise is "stop it, and prove what happened." Until now a human operator could _read_ governance state but had no first-class, audited way to pull authority — revoke lived only in the SDK an agent holds. Phase 2's revoke route makes containment a human break-glass action: a tenant owner/admin can revoke any grant in their tenant, and because the human is never a chain principal, every human revocation is stamped into the tamper-evident lifecycle chain with human_break_glass + admin_override markers — forensically distinct from an issuer/ancestor-subject revocation, and impossible to silently undo.

How It Works

Each route reads tenantCtx set by supabaseJwtAuthMiddleware (newly mounted on /auth/grants and /auth/grants/* in registerPreAuthRoutes) and delegates to the same store + shared cores the apiKey routes use (getGrantDeps, evaluateGrantCheckVerdict, recordGrantLifecycle, projectGrantRow). Issue and attenuate are deliberately absent — agents hold credentials and mint authority over the SDK; humans observe, simulate, and contain.

Revoke is gated twice: the capability declares permission: grants.revoke, and the handler additionally requires membershipRole ∈ {owner, admin} — a plain member can read and simulate but never pull authority. The lifecycle row is written on every revoke attempt, including an idempotent re-revoke that flips nothing (already_revoked: true), so a direct-DB un-revoke followed by a re-revoke is detectable.

The Numbers

  • 5 new JWT-human routes; 6 new tests (capability-inverse exact+unrestricted matching, the break-glass role gate, tenant-auth 401, not-found 404, audited revoke).
  • 9,019 unit tests pass; OpenAPI (docs/openapi.yaml) + static-assets + MCP manifest regenerated and in sync; pnpm check green.
  • 0 new SDK/MCP surface — these are dashboard-internal mirrors of the already-published /v1/grants (TS/Py SDK grants.) and br_grant_ MCP tools.

Competitive Edge

A revoke button is table stakes. A revoke button whose every press is cryptographically committed to a tamper-evident chain — distinguishable as a human break-glass override, un-undoable without detection — is the "evidence plane" thesis applied to the operator's own most powerful action. Portkey/OpenRouter/Lasso have dashboards; none can prove who pulled authority and when.

Lockstep Checklist

  • [x] API Routes: src/api/capabilities/auth/grants.ts (5 routes), registered in _registry.ts, JWT mounted in register-routes.ts.
  • [x] TS SDK: no change — JWT dashboard mirror of the existing /v1/grants SDK surface (grants.* already published in brainstormrouter 0.1.1).
  • [x] Python SDK: no change (same rationale).
  • [x] MCP Schemas: no change — agent grant surface (br_grant_*) unchanged.
  • [x] Generated contract: docs/openapi.yaml (510 paths) + static-assets.generated.ts regenerated; validate:manifest in sync.

Remaining (Phase 2 frontend + Phases 3–4)

This is the backend gate only. The Phase 2 dashboard build (W2.1–W2.10: api/grants.ts over the SDK, #grants list + "Who can…" tab, grant-chain-tree.ts, detail panel + evidence bundle, revoke flow + cascade animation, client JWKS verify with server-attested fallback, /check-fed simulator, API-keys grant-bound column, capability-detected nav) and Phases 3 (evidence honest) and 4 (Plans) remain.