Files
stack/docs/PRD-MS21.md
2026-02-28 17:12:22 +00:00

14 KiB

PRD: MS21 — Multi-Tenant Platform, RBAC Enforcement, and Data Migration

Metadata

  • Owner: Jason Woltje
  • Orchestrator: Jarvis (OpenClaw)
  • Date: 2026-02-28
  • Status: in-progress
  • Version: 0.0.21

Problem Statement

Mosaic Stack is a single-user deployment. The Workspace, Team, WorkspaceMember, and RBAC infrastructure exist in the schema and codebase (PermissionGuard, WorkspaceGuard, roles: OWNER/ADMIN/MEMBER/GUEST), but there is no admin UI for managing users, workspaces, or teams. There is no invitation flow, no user management page, and no break-glass authentication mechanism for emergency access without OIDC. Additionally, the platform has no real operational data — jarvis-brain contains 106 projects and 95 tasks that need migrating into Mosaic Stack to make the dashboard, kanban, and project pages useful.

Objectives

  1. Build admin UI for user management (list, invite, deactivate, assign roles)
  2. Build workspace management UI (create, configure, manage members)
  3. Build team management UI (create teams within workspaces, assign members)
  4. Implement user invitation flow (email or link-based)
  5. Implement break-glass local authentication (bypass OIDC for emergency access)
  6. Enforce RBAC across all UI surfaces (show/hide based on role)
  7. Build admin Settings pages for workspace and platform configuration
  8. Migrate jarvis-brain data (95 tasks, 106 projects) into Mosaic Stack PostgreSQL
  9. Build data import API endpoint for bulk task/project creation

Completed Work

Existing Infrastructure (Already Built)

Component Status Location
Prisma models: User, Workspace, WorkspaceMember, Team, TeamMember Complete apps/api/prisma/schema.prisma
WorkspaceMemberRole enum (OWNER, ADMIN, MEMBER, GUEST) Complete schema.prisma
TeamMemberRole enum (OWNER, ADMIN, MEMBER) Complete schema.prisma
PermissionGuard with role hierarchy Complete apps/api/src/common/guards/permission.guard.ts
Permission decorator (@RequirePermission) Complete apps/api/src/common/decorators/permissions.decorator.ts
Permission enum (WORKSPACE_OWNER, ADMIN, MEMBER, ANY) Complete permissions.decorator.ts
WorkspaceGuard Complete apps/api/src/common/guards/workspace.guard.ts
RBAC applied to 18+ controllers Complete All resource controllers
Workspaces CRUD API Complete apps/api/src/workspaces/
BetterAuth + Authentik OIDC Complete apps/api/src/auth/
AdminGuard Complete apps/api/src/auth/guards/admin.guard.ts

Scope

In Scope (MS21)

S1: Admin API Endpoints (Backend)

  1. GET /api/admin/users — List all users with workspace memberships and roles
  2. POST /api/admin/users/invite — Generate invitation (email or link)
  3. PATCH /api/admin/users/:id — Update user metadata (deactivate, change global role)
  4. DELETE /api/admin/users/:id — Deactivate user (soft delete, preserve data)
  5. POST /api/admin/workspaces — Create workspace with owner assignment
  6. PATCH /api/admin/workspaces/:id — Update workspace settings
  7. POST /api/workspaces/:id/members — Add member to workspace with role
  8. PATCH /api/workspaces/:id/members/:userId — Change member role
  9. DELETE /api/workspaces/:id/members/:userId — Remove member from workspace
  10. POST /api/workspaces/:id/teams — Create team within workspace
  11. POST /api/workspaces/:id/teams/:teamId/members — Add member to team
  12. DELETE /api/workspaces/:id/teams/:teamId/members/:userId — Remove from team
  13. POST /api/import/tasks — Bulk import tasks from jarvis-brain format
  14. POST /api/import/projects — Bulk import projects from jarvis-brain format

S2: Break-Glass Authentication

  1. Add isLocalAuth boolean field to User model (Prisma migration)
  2. Add passwordHash optional field to User model (for local auth users)
  3. Implement /api/auth/local/login endpoint (email + password, bcrypt)
  4. Implement /api/auth/local/setup endpoint (first-time break-glass user creation, requires admin token from env)
  5. Break-glass user bypasses OIDC entirely — session-based auth via BetterAuth
  6. Environment variable BREAKGLASS_SETUP_TOKEN controls initial setup access

S3: Admin UI Pages (Frontend)

  1. /settings/users — User management page (table: name, email, role, status, workspaces, last login)
  2. User detail/edit dialog (change role, deactivate, view workspace memberships)
  3. Invite user dialog (email input, workspace selection, role selection)
  4. /settings/workspaces — Workspace management page (list workspaces, member counts)
  5. Workspace detail page (members list, team list, settings)
  6. Add/remove workspace member dialog with role picker
  7. /settings/teams — Team management within workspace context
  8. Create team dialog, add/remove team members

S4: RBAC UI Enforcement

  1. Navigation sidebar: show/hide admin items based on user role
  2. Settings pages: restrict access to admin-only pages
  3. Action buttons: disable/hide create/delete based on permission level
  4. User profile: show current role and workspace memberships

S5: Data Migration

  1. Build scripts/migrate-brain.ts — TypeScript migration script
  2. Read jarvis-brain data/tasks/*.json (v2.0 format: { version, domain, tasks: [...] })
  3. Read jarvis-brain data/projects/*.json (format: { version, project: {...}, tasks: [...] })
  4. Map brain status to Mosaic TaskStatus (done->COMPLETED, in-progress->IN_PROGRESS, backlog/pending/scheduled/not-started/planned->NOT_STARTED, blocked/on-hold->PAUSED, cancelled->ARCHIVED)
  5. Map brain priority to Mosaic TaskPriority (critical/high->HIGH, medium->MEDIUM, low->LOW)
  6. Create Domain records for unique domains (work, homelab, finances, family, etc.)
  7. Create Project records from project data, linking to domains
  8. Create Task records linking to projects, preserving brain-specific fields in metadata JSON
  9. Preserve blocks, blocked_by, repo, branch, current_milestone, notes in task/project metadata
  10. Dry-run mode with validation report before actual import
  11. Idempotent — skip records that already exist (match by metadata.brainId)

Out of Scope

  • Federation (MS22)
  • Agent task mapping and telemetry (MS23)
  • Playwright E2E tests (MS24)
  • Email delivery service (invitations stored as links for now)
  • User self-registration (admin-only invitation model)

User/Stakeholder Requirements

  1. Jason (admin) can invite Melanie to a shared workspace
  2. Jason can create a "USC IT" workspace and invite employees
  3. Each user sees only their workspace(s) data
  4. Break-glass user can log in without Authentik being available
  5. Admin pages are only visible to OWNER/ADMIN roles
  6. Data from jarvis-brain appears in the dashboard, kanban, and project pages after migration
  7. All existing tests continue to pass after changes

Functional Requirements

FR-001: Admin User Management API

  • AdminGuard protects all /api/admin/* routes
  • List users returns: id, name, email, emailVerified, createdAt, workspace memberships with roles
  • Invite creates a User record with a pending invitation token
  • Deactivate sets a deactivatedAt timestamp (new field), does not delete data
  • ASSUMPTION: User deactivation is soft-delete via new deactivatedAt field on User model. Rationale: Hard delete would cascade and destroy workspace data.

FR-002: Workspace Member Management API

  • Add member requires WORKSPACE_ADMIN or WORKSPACE_OWNER permission
  • Role changes require equal or higher permission (MEMBER cannot promote to ADMIN)
  • Cannot remove the last OWNER of a workspace
  • Cannot change own role to lower than OWNER if sole owner

FR-003: Break-Glass Authentication

  • Local auth is opt-in per deployment via ENABLE_LOCAL_AUTH=true env var
  • Break-glass setup requires a one-time setup token from environment
  • Password stored as bcrypt hash, minimum 12 characters
  • Break-glass user gets OWNER role on default workspace
  • Session management identical to OIDC users (BetterAuth session tokens)
  • ASSUMPTION: BetterAuth supports custom credential providers alongside OIDC. Rationale: BetterAuth documentation confirms credential-based auth as a built-in feature.

FR-004: Admin UI

  • User management table with search, sort, filter by role/status
  • Inline role editing via dropdown
  • Confirmation dialog for destructive actions (deactivate user, remove from workspace)
  • PDA-friendly language: "Deactivate" not "Delete", "Pending" not "Expired"
  • Responsive design matching existing Settings page patterns
  • Dark/light theme support via existing design token system

FR-005: Data Migration Script

  • Standalone TypeScript script in scripts/migrate-brain.ts
  • Reads from jarvis-brain path (configurable via --brain-path flag)
  • Connects to Mosaic Stack database via DATABASE_URL
  • Requires target workspace ID (--workspace-id flag)
  • Requires creator user ID (--user-id flag)
  • Outputs validation report before writing (dry-run by default)
  • --apply flag to execute the migration
  • Creates Activity log entries for all imported records

Technical Design

Schema Changes (Prisma Migration)

Add to User model:

  • deactivatedAt DateTime? (soft delete)
  • isLocalAuth Boolean default(false)
  • passwordHash String? (bcrypt hash for local auth)
  • invitedBy String? Uuid (FK to inviting user)
  • invitationToken String? unique
  • invitedAt DateTime?

New NestJS Module: AdminModule

Location: apps/api/src/admin/ Files: admin.module.ts, admin.controller.ts, admin.service.ts, DTOs, specs

New NestJS Module: LocalAuthModule (Break-Glass)

Location: apps/api/src/auth/local/ Files: local-auth.controller.ts, local-auth.service.ts, DTOs, specs

Frontend Pages

Location: apps/web/app/(authenticated)/settings/

  • users/page.tsx — User management
  • workspaces/page.tsx — Workspace list
  • workspaces/[id]/page.tsx — Workspace detail (members, teams)
  • teams/page.tsx — Team management

Testing and Verification

  1. Baseline: pnpm lint && pnpm build && pnpm test must pass
  2. Unit tests for AdminService (user CRUD, invitation flow, permission checks)
  3. Unit tests for LocalAuthService (bcrypt hashing, token validation)
  4. Unit tests for AdminController (route guards, DTO validation)
  5. Integration test: invitation -> acceptance -> workspace access
  6. Integration test: break-glass setup -> login -> workspace access
  7. Integration test: RBAC prevents unauthorized role changes
  8. Migration script test: dry-run produces valid report, apply creates correct records
  9. Frontend: admin pages render with correct role gating
  10. All 4,772+ existing tests continue to pass

Quality Gates

pnpm lint && pnpm build && pnpm test

Delivery Phases

Phase Focus Tasks
P1: Schema + Admin API Prisma migration, AdminModule, user/workspace/team management endpoints S1 items 1-12
P2: Break-Glass Auth LocalAuthModule, credential provider, setup flow S2 items 15-20
P3: Data Migration Migration script, jarvis-brain to PostgreSQL S5 items 33-43
P4: Admin UI Settings pages for users, workspaces, teams S3 items 21-28
P5: RBAC UI Enforcement Frontend permission gating, navigation filtering S4 items 29-32
P6: Verification Full test pass, deployment, smoke test All quality gates

Risks and Open Questions

  1. Risk: BetterAuth credential provider may require specific adapter configuration. Mitigation: Review BetterAuth docs for credential auth alongside OIDC; test in isolation first.
  2. Risk: Schema migration on production database. Mitigation: Use Prisma migration with --create-only for review before applying.
  3. Risk: jarvis-brain data has fields that don't map 1:1 to Mosaic schema. Mitigation: Use metadata JSON field for overflow; validate with dry-run.
  4. Open: Should deactivated users retain active sessions? ASSUMPTION: No, deactivation immediately invalidates all sessions. Rationale: Security best practice.
  5. Open: Should break-glass user be auto-created on first deployment? ASSUMPTION: No, requires explicit setup via API with env token. Rationale: Prevents accidental exposure.

Assumptions

  1. User deactivation is soft-delete via deactivatedAt field. Hard delete cascades and destroys workspace data.
  2. BetterAuth supports custom credential providers alongside OIDC.
  3. Invitation flow uses link-based tokens (no email service required initially).
  4. Break-glass auth is off by default, controlled by ENABLE_LOCAL_AUTH env var.
  5. Migration script runs outside the API server as a standalone script.
  6. Admin pages follow existing Settings page UI patterns (cards, tables, dialogs).