diff --git a/TURNOVER.md b/TURNOVER.md index c61dd1d..c0e6736 100644 --- a/TURNOVER.md +++ b/TURNOVER.md @@ -1,87 +1,85 @@ # Switchboard Core — Session Turnover **Date**: 2026-03-26 **Author**: Jeffrey Smith (jasafpro@gmail.com) -**Session**: Platform pivot gut — Phase 0 completion +**Session**: v0.2.0 PR 1 — Full RBAC migration --- ## What Was Done -### Phase 0 (Steps 1-10) — COMPLETE -Forked from chat-switchboard v0.38.5, gutted all AI/chat features, rebranded -as Switchboard Core — a pure extension platform kernel. +### PR 1: Admin → RBAC group migration — COMPLETE +Branch `feat/admin-rbac-migration` (PR #1), 3 commits: -**By the numbers:** -- Started: ~800 files, ~100K+ lines -- Now: 462 files, ~75K lines -- Removed: ~44K lines of chat/notes/projects/provider code -- 15 Go packages deleted, 29 handler files removed -- Store interfaces: 40 → 20 -- Kernel permissions: 16 → 6 +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. -### Key commits this session: -- Steps 5-8: deep gut of code, build clean, tests pass -- Step 9: single Docker image CI/CD, drop FE/BE split -- Step 10: K8s deploy live, nginx BASE_PATH fixes, login→admin verified -- Branding: "Chat Switchboard" → "Switchboard Core" +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. -### Deployment state: -- **CI**: Green on Gitea Actions (build + test + deploy) -- **K8s**: `switchboard.gobha.ai/test` — live in `gobha-ai-switchboard` namespace -- **DB**: Postgres connected, schema at 009_ha.sql -- **Storage**: PVC (S3 keys not configured yet) -- **Old namespace**: `gobha-ai-chat` — stale ingresses cleaned up +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**: Some gutted chat/model/channel tests were removed - but the FE test suite may have remaining stale references +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 for retry middleware (non-blocking) -3. **`SEED_USERS`**: Not set in K8s secrets — no initial admin user created - on the test deployment. Set in Gitea secrets to bootstrap. -4. **Editor modules**: `src/editor/*.mjs` files still reference "Chat - Switchboard" — these are CodeMirror build files, low priority + 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 -### Protect main (DO NOW) -Phase 0 is complete. Main should be protected. All future work via PRs. +### Merge PR #1 +Review and merge `feat/admin-rbac-migration` to main. -### v0.2.0 — First PRs - -**PR 1: Admin → RBAC group migration** -- Add `surface.admin.access` to permissions table -- Seed "Admins" group with that grant -- Seed "Everyone" group, auto-member all users -- Change admin middleware: role check → grant check -- **Edit existing migration SQL files in place** (no new migrations pre-MVP) +### 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) -- `user_overridable` controls whether lower scope can override higher - Resolution: user → team → global (first non-null wins) **PR 3: ICD (API contract)** - Generate full OpenAPI spec from registered routes - Document kernel-only endpoints -- Mark extension API surface **PR 4: Trigger system** -- Time triggers (cron) -- Webhook triggers (inbound HTTP) -- Event triggers (bus subscription) +- 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 -- Primitive UI components (toast, confirm, prompt, dialog) --- @@ -89,12 +87,13 @@ Phase 0 is complete. Main should be protected. All future work via PRs. | 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. | -| Admin → RBAC group | `role` column replaced by "Admins" group + grant. | -| Settings: two axes | RBAC = who can change at what scope. `user_overridable` = can lower scope override. Orthogonal. | -| Default surface | No surfaces → admin is default. First installed becomes default. Configurable. | -| Protect main after Step 7+ | Destructive surgery done on main. Additive work (ICD, triggers) goes through PRs. | -| Single Docker image | nginx + Go binary + static assets + migrations. One image, one deployment. | +| 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. | --- @@ -105,14 +104,18 @@ Phase 0 is complete. Main should be protected. All future work via PRs. | Roadmap | `ROADMAP.md` | | Changelog | `CHANGELOG.md` | | CI workflow | `.gitea/workflows/ci.yaml` | -| K8s manifests | `k8s/` (switchboard.yaml, ingress.yaml, storage-pvc.yaml) | +| K8s manifests | `k8s/` | | Docker compose (local dev) | `docker-compose.yml` (SQLite, port 3000) | | Nginx template | `nginx.conf.template` | -| Entrypoint | `docker-entrypoint.sh` | -| Migrations | `server/migrations/postgres/`, `server/migrations/sqlite/` | -| Go backend | `server/` | -| Frontend shell | `src/` | -| Templates | `server/pages/templates/` | +| 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/` | ---