This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/TURNOVER.md
Jeffrey Smith a830ae3684
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-frontend (push) Has been skipped
CI/CD / test-sqlite (push) Has been skipped
CI/CD / test-go-pg (push) Has been skipped
CI/CD / build-and-deploy (push) Has been skipped
Feat admin rbac migration (#2)
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
2026-03-26 17:32:32 +00:00

133 lines
5.4 KiB
Markdown

# Switchboard Core — Session Turnover
**Date**: 2026-03-26
**Author**: Jeffrey Smith (jasafpro@gmail.com)
**Session**: v0.2.0 PR 1 — Full RBAC migration
---
## What Was Done
### PR 1: Admin → RBAC group migration — COMPLETE
Branch `feat/admin-rbac-migration` (PR #1), 3 commits:
1. **Admin → RBAC group migration**`surface.admin.access` permission +
Admins system group replaces hardcoded `role == "admin"` checks. Admin
bypass removed from `RequirePermission`. Bootstrap/seed/OIDC/admin handlers
sync group membership.
2. **Remove token budgets + allowed models from groups** — Provider-era cruft:
`token_budget_daily`, `token_budget_monthly`, `allowed_models` columns,
`ResolveTokenBudget()`, `ResolveModelAllowlist()`, and all store/handler/UI
code deleted. -283 lines.
3. **Drop `users.role` column** — Full RBAC. Zero magic roles. The `role` column,
`UserRoleAdmin`/`UserRoleUser` constants, `CountByRole()`, `DefaultRole` config,
`role` in JWT claims, and all role-based shortcuts removed. Everyone group
membership is now explicit (all users added on create). 28 files changed.
**Net result: -364 lines across 3 commits.**
### Architecture after this PR:
- **Everyone group** (`00000000...0001`): all users explicitly added on creation.
Carries `extension.use`, `workflow.submit`.
- **Admins group** (`00000000...0002`): carries all 7 kernel permissions including
`surface.admin.access`. Not special-cased — just a group with permissions.
- **Permission resolution**: union of all group memberships. No implicit groups,
no role shortcuts, no admin bypass.
- **Admin middleware**: checks `surface.admin.access` grant via `resolveAndCachePerms`.
- **JWT claims**: `user_id` + `email` only. No role.
- **OIDC**: `isIdPAdmin()` maps IdP role claim → Admins group membership.
No `role` column writes.
- **User creation paths**: builtin register, OIDC auto-provision, mTLS auto-provision,
admin create, bootstrap, seed — all call `EnsureEveryoneGroup()`.
---
## Known Issues
1. **Frontend test job**: FE test suite may have remaining stale references
from Phase 0 gut (pre-existing)
2. **Traefik middleware**: `rbac-traefik.yaml` needs one-time manual apply
by cluster admin (pre-existing, non-blocking)
3. **`SEED_USERS`**: Not set in K8s secrets (pre-existing)
4. **Editor modules**: `src/editor/*.mjs` still reference "Chat Switchboard"
(pre-existing, low priority)
5. **K8s deploy**: PR not yet merged to main — CI/CD will need a deploy after merge
---
## What's Next
### Merge PR #1
Review and merge `feat/admin-rbac-migration` to main.
### v0.2.0 — Remaining PRs
**PR 2: Settings cascade**
- Add `user_overridable` flag to settings schema
- RBAC controls scope auth (admin → global, team-admin → team, user → personal)
- Resolution: user → team → global (first non-null wins)
**PR 3: ICD (API contract)**
- Generate full OpenAPI spec from registered routes
- Document kernel-only endpoints
**PR 4: Trigger system**
- Time triggers (cron), webhook (inbound HTTP), event (bus subscription)
- Extensions register match expressions at install
**PR 5: SDK stabilization**
- `sb.slots()`, `sb.actions`, `sb.api.ext()`, `sb.storage`
- Theme tokens exposed to extensions
---
## Architecture Decisions (this session)
| Decision | Detail |
|----------|--------|
| Full RBAC — no magic roles | `users.role` column dropped entirely. All authorization through group membership + permission grants. |
| Explicit Everyone membership | No implicit "all users get Everyone perms". Every user added to Everyone group on create. `member_count` reflects reality. |
| Admins group not special-cased | Code never checks group identity — only checks for `surface.admin.access` permission. Any group can grant it. |
| No new migrations pre-MVP | Edit existing SQL files in place. Schema isn't in production. |
| JWT simplified | Claims carry `user_id` + `email` only. Permissions resolved server-side from groups on every request. |
| OIDC role → group sync | `isIdPAdmin()` replaces `resolveRole()`. IdP admin claim maps to Admins group membership, not a DB column. |
| Token budgets/allowed models removed | Provider-era cruft. Belongs in a future provider extension, not the kernel. |
---
## File Locations
| What | Where |
|------|-------|
| Roadmap | `ROADMAP.md` |
| Changelog | `CHANGELOG.md` |
| CI workflow | `.gitea/workflows/ci.yaml` |
| K8s manifests | `k8s/` |
| Docker compose (local dev) | `docker-compose.yml` (SQLite, port 3000) |
| Nginx template | `nginx.conf.template` |
| Migrations | `server/database/migrations/{postgres,sqlite}/` |
| Permission constants | `server/auth/permissions.go` |
| Admin middleware | `server/middleware/admin.go` |
| Group store | `server/store/{postgres,sqlite}/groups.go` |
| Bootstrap/seed | `server/handlers/auth.go` |
| Admin handlers | `server/handlers/admin.go` |
| Groups UI | `src/js/sw/surfaces/admin/groups.js` |
| Users UI | `src/js/sw/surfaces/admin/users.js` |
| SDK API | `src/js/sw/sdk/api-domains.js` |
| Memory (Claude) | `.claude/projects/-config-Projects-core/memory/` |
---
## Gitea Secrets/Vars Needed
**Secrets**: `POSTGRES_USER`, `POSTGRES_USER_PASSWORD`, `POSTGRES_ADMIN_USER`,
`POSTGRES_ADMIN_PASSWORD`, `ENCRYPTION_KEY`
**Vars**: `DOMAIN`, `NAMESPACE`, `S3_BUCKET`, `S3_ENDPOINT`, `STORAGE_BACKEND`,
`STORAGE_CLASS`, `STORAGE_SIZE`
**Not yet configured**: `S3_ACCESS_KEY`, `S3_SECRET_KEY` (using PVC for now),
`SEED_USERS` (needed for initial admin bootstrap)