WS8 PR B: firm policy enforcement (keys + connectors)

↗ view on GitHub · sloth-ninja · 2026-07-22 · 5d524088

Make the two firm policies from PR C real: block the matching personal
write routes when a firm's policy is OFF, hide the matching member UI, and
let admins flip the policies live from Firm settings. Orgless self-hosters
and policy-ON firms are unchanged. No migration, no new env vars.

Backend:
- requireMemberPolicy(policy, detail) middleware (middleware/auth.ts) reuses
  resolveUserOrganisation; orgless + policy-ON pass through, policy-OFF gets a
  fixed 403. Admins are NOT exempt (they use the firm surface). Fails OPEN on
  any lookup error (availability > a brief self-correcting policy gap), fully
  try-wrapped so it can never leak an unhandled rejection or hang a request.
- Gated writes (routes/user.ts): PUT /user/api-keys/:provider (+null-save
  delete); POST /mcp-connectors, PATCH/DELETE /mcp-connectors/:id, POST
  /mcp-connectors/:id/oauth/start. Reads + refresh/tool-toggle stay open so
  existing connectors keep working in chat.
- organisations.updateOrganisationPolicies + PATCH /admin/policies
  (requireAdmin + MFA), scoped to the caller's own firm.

Frontend:
- account/layout.tsx hides the API Keys / Connectors tabs per policy; direct
  nav renders a neutral "Managed by your firm" card. Model Preferences shows a
  firm-provided note; company-search empty-state points policy-OFF members at
  their firm admin. Firm settings Policies card is now live AccountToggles
  (optimistic + rollback, MFA-guarded, reloadProfile on success).

Tests: backend 215 pass (new middleware/auth.policy.test.ts policy matrix +
extended admin.test.ts PATCH /policies authz/MFA/validation/persistence).
Frontend tsc + eslint clean on changed files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Repository Sloth-ninja/JessicaOSS
Author sloth-ninja <ezanahaddis@gmail.com>
Authored
Parents a0e3e046
Stats 17 files changed , +812 , -67
Part of WS8 PR B: firm policy enforcement (keys + connectors)

Capture this commit into my fork

Download a Markdown prompt that tells Claude how to port this exact commit into your working tree. Run it via claude -p < capture-commit-5d524088.md from inside the repo you want the change in.

⬇ Download capture-commit-5d524088.md