WS8 PR C: firm API keys, admin area, member roles

↗ view on GitHub · sloth-ninja · 2026-07-22 · 9fba26ab

What
- Firm-level (shared) provider API keys managed by admins, layered into
  key resolution as user > firm > env. Orgless/unmigrated deployments are
  unchanged.
- New /admin area (routes/admin.ts) behind requireAuth + requireAdmin:
  GET/PUT firm-keys, GET members, PATCH member role (MFA-gated mutations).
- Admin "Firm settings" screen: members (role badges, promote/demote with
  confirm + MFA), firm API keys (per-provider rows), read-only Policies
  preview. Sidebar gains a "Firm admin" group, visible only to admins.

Why
- First user-visible firm-administration surface for the pilot, building on
  the PR A (#34) organisation foundation. Lets a firm provision one shared
  set of provider keys for all members while a member's own key still wins.

How
- Shared AES-256-GCM crypto extracted to lib/apiKeyCrypto.ts (scheme
  unchanged); userApiKeys.ts keeps thin wrappers. Firm-key CRUD in
  organisationApiKeys.ts. Member listing + setMemberRole (org-scoped writes,
  last-admin guard) in organisations.ts. No migration - organisation_api_keys
  already exists from PR A.
- account/api-keys surfaces the new "firm" source ("Provided by your firm").

Tested
- Backend: tsc clean; vitest 199/199 (rewrote userApiKeys precedence matrix;
  new organisationApiKeys, organisationMembers, routes/admin tests covering
  authz 403, last-admin 409, cross-firm scoping 404).
- Frontend: tsc clean; eslint src 34 errors/77 warnings - identical to main
  baseline (changed files add zero).
- evals:smoke from the main checkout: 4/4 pass.
- Independent code review: no blockers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Repository Sloth-ninja/JessicaOSS
Author sloth-ninja <ezanahaddis@gmail.com>
Authored
Parents d52ffc01
Stats 16 files changed , +2011 , -102
Part of WS8 PR C: firm API keys, admin area, member roles

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-9fba26ab.md from inside the repo you want the change in.

⬇ Download capture-commit-9fba26ab.md