All checks were successful
CI/CD / detect-changes (pull_request) Successful in 3s
CI/CD / test-sqlite (pull_request) Has been skipped
CI/CD / test-go-pg (pull_request) Has been skipped
CI/CD / test-frontend (pull_request) Has been skipped
CI/CD / build-and-deploy (pull_request) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
133 lines
5.4 KiB
Markdown
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)
|