Feat admin rbac migration (#2)
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com> Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #2.
This commit is contained in:
115
TURNOVER.md
115
TURNOVER.md
@@ -1,87 +1,85 @@
|
|||||||
# Switchboard Core — Session Turnover
|
# Switchboard Core — Session Turnover
|
||||||
**Date**: 2026-03-26
|
**Date**: 2026-03-26
|
||||||
**Author**: Jeffrey Smith (jasafpro@gmail.com)
|
**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
|
## What Was Done
|
||||||
|
|
||||||
### Phase 0 (Steps 1-10) — COMPLETE
|
### PR 1: Admin → RBAC group migration — COMPLETE
|
||||||
Forked from chat-switchboard v0.38.5, gutted all AI/chat features, rebranded
|
Branch `feat/admin-rbac-migration` (PR #1), 3 commits:
|
||||||
as Switchboard Core — a pure extension platform kernel.
|
|
||||||
|
|
||||||
**By the numbers:**
|
1. **Admin → RBAC group migration** — `surface.admin.access` permission +
|
||||||
- Started: ~800 files, ~100K+ lines
|
Admins system group replaces hardcoded `role == "admin"` checks. Admin
|
||||||
- Now: 462 files, ~75K lines
|
bypass removed from `RequirePermission`. Bootstrap/seed/OIDC/admin handlers
|
||||||
- Removed: ~44K lines of chat/notes/projects/provider code
|
sync group membership.
|
||||||
- 15 Go packages deleted, 29 handler files removed
|
|
||||||
- Store interfaces: 40 → 20
|
|
||||||
- Kernel permissions: 16 → 6
|
|
||||||
|
|
||||||
### Key commits this session:
|
2. **Remove token budgets + allowed models from groups** — Provider-era cruft:
|
||||||
- Steps 5-8: deep gut of code, build clean, tests pass
|
`token_budget_daily`, `token_budget_monthly`, `allowed_models` columns,
|
||||||
- Step 9: single Docker image CI/CD, drop FE/BE split
|
`ResolveTokenBudget()`, `ResolveModelAllowlist()`, and all store/handler/UI
|
||||||
- Step 10: K8s deploy live, nginx BASE_PATH fixes, login→admin verified
|
code deleted. -283 lines.
|
||||||
- Branding: "Chat Switchboard" → "Switchboard Core"
|
|
||||||
|
|
||||||
### Deployment state:
|
3. **Drop `users.role` column** — Full RBAC. Zero magic roles. The `role` column,
|
||||||
- **CI**: Green on Gitea Actions (build + test + deploy)
|
`UserRoleAdmin`/`UserRoleUser` constants, `CountByRole()`, `DefaultRole` config,
|
||||||
- **K8s**: `switchboard.gobha.ai/test` — live in `gobha-ai-switchboard` namespace
|
`role` in JWT claims, and all role-based shortcuts removed. Everyone group
|
||||||
- **DB**: Postgres connected, schema at 009_ha.sql
|
membership is now explicit (all users added on create). 28 files changed.
|
||||||
- **Storage**: PVC (S3 keys not configured yet)
|
|
||||||
- **Old namespace**: `gobha-ai-chat` — stale ingresses cleaned up
|
**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
|
## Known Issues
|
||||||
|
|
||||||
1. **Frontend test job**: Some gutted chat/model/channel tests were removed
|
1. **Frontend test job**: FE test suite may have remaining stale references
|
||||||
but the 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
|
2. **Traefik middleware**: `rbac-traefik.yaml` needs one-time manual apply
|
||||||
by cluster admin for retry middleware (non-blocking)
|
by cluster admin (pre-existing, non-blocking)
|
||||||
3. **`SEED_USERS`**: Not set in K8s secrets — no initial admin user created
|
3. **`SEED_USERS`**: Not set in K8s secrets (pre-existing)
|
||||||
on the test deployment. Set in Gitea secrets to bootstrap.
|
4. **Editor modules**: `src/editor/*.mjs` still reference "Chat Switchboard"
|
||||||
4. **Editor modules**: `src/editor/*.mjs` files still reference "Chat
|
(pre-existing, low priority)
|
||||||
Switchboard" — these are CodeMirror build files, low priority
|
5. **K8s deploy**: PR not yet merged to main — CI/CD will need a deploy after merge
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What's Next
|
## What's Next
|
||||||
|
|
||||||
### Protect main (DO NOW)
|
### Merge PR #1
|
||||||
Phase 0 is complete. Main should be protected. All future work via PRs.
|
Review and merge `feat/admin-rbac-migration` to main.
|
||||||
|
|
||||||
### v0.2.0 — First PRs
|
### v0.2.0 — Remaining 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)
|
|
||||||
|
|
||||||
**PR 2: Settings cascade**
|
**PR 2: Settings cascade**
|
||||||
- Add `user_overridable` flag to settings schema
|
- Add `user_overridable` flag to settings schema
|
||||||
- RBAC controls scope auth (admin → global, team-admin → team, user → personal)
|
- 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)
|
- Resolution: user → team → global (first non-null wins)
|
||||||
|
|
||||||
**PR 3: ICD (API contract)**
|
**PR 3: ICD (API contract)**
|
||||||
- Generate full OpenAPI spec from registered routes
|
- Generate full OpenAPI spec from registered routes
|
||||||
- Document kernel-only endpoints
|
- Document kernel-only endpoints
|
||||||
- Mark extension API surface
|
|
||||||
|
|
||||||
**PR 4: Trigger system**
|
**PR 4: Trigger system**
|
||||||
- Time triggers (cron)
|
- Time triggers (cron), webhook (inbound HTTP), event (bus subscription)
|
||||||
- Webhook triggers (inbound HTTP)
|
|
||||||
- Event triggers (bus subscription)
|
|
||||||
- Extensions register match expressions at install
|
- Extensions register match expressions at install
|
||||||
|
|
||||||
**PR 5: SDK stabilization**
|
**PR 5: SDK stabilization**
|
||||||
- `sb.slots()`, `sb.actions`, `sb.api.ext()`, `sb.storage`
|
- `sb.slots()`, `sb.actions`, `sb.api.ext()`, `sb.storage`
|
||||||
- Theme tokens exposed to extensions
|
- 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 |
|
| 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. |
|
| 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. |
|
| JWT simplified | Claims carry `user_id` + `email` only. Permissions resolved server-side from groups on every request. |
|
||||||
| Settings: two axes | RBAC = who can change at what scope. `user_overridable` = can lower scope override. Orthogonal. |
|
| OIDC role → group sync | `isIdPAdmin()` replaces `resolveRole()`. IdP admin claim maps to Admins group membership, not a DB column. |
|
||||||
| Default surface | No surfaces → admin is default. First installed becomes default. Configurable. |
|
| Token budgets/allowed models removed | Provider-era cruft. Belongs in a future provider extension, not the kernel. |
|
||||||
| 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. |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -105,14 +104,18 @@ Phase 0 is complete. Main should be protected. All future work via PRs.
|
|||||||
| Roadmap | `ROADMAP.md` |
|
| Roadmap | `ROADMAP.md` |
|
||||||
| Changelog | `CHANGELOG.md` |
|
| Changelog | `CHANGELOG.md` |
|
||||||
| CI workflow | `.gitea/workflows/ci.yaml` |
|
| 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) |
|
| Docker compose (local dev) | `docker-compose.yml` (SQLite, port 3000) |
|
||||||
| Nginx template | `nginx.conf.template` |
|
| Nginx template | `nginx.conf.template` |
|
||||||
| Entrypoint | `docker-entrypoint.sh` |
|
| Migrations | `server/database/migrations/{postgres,sqlite}/` |
|
||||||
| Migrations | `server/migrations/postgres/`, `server/migrations/sqlite/` |
|
| Permission constants | `server/auth/permissions.go` |
|
||||||
| Go backend | `server/` |
|
| Admin middleware | `server/middleware/admin.go` |
|
||||||
| Frontend shell | `src/` |
|
| Group store | `server/store/{postgres,sqlite}/groups.go` |
|
||||||
| Templates | `server/pages/templates/` |
|
| 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/` |
|
| Memory (Claude) | `.claude/projects/-config-Projects-core/memory/` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user