Implements FED-010: Agent Spawn via Federation feature that enables spawning and managing Claude agents on remote federated Mosaic Stack instances via COMMAND message type. Features: - Federation agent command types (spawn, status, kill) - FederationAgentService for handling agent operations - Integration with orchestrator's agent spawner/lifecycle services - API endpoints for spawning, querying status, and killing agents - Full command routing through federation COMMAND infrastructure - Comprehensive test coverage (12/12 tests passing) Architecture: - Hub → Spoke: Spawn agents on remote instances - Command flow: FederationController → FederationAgentService → CommandService → Remote Orchestrator - Response handling: Remote orchestrator returns agent status/results - Security: Connection validation, signature verification Files created: - apps/api/src/federation/types/federation-agent.types.ts - apps/api/src/federation/federation-agent.service.ts - apps/api/src/federation/federation-agent.service.spec.ts Files modified: - apps/api/src/federation/command.service.ts (agent command routing) - apps/api/src/federation/federation.controller.ts (agent endpoints) - apps/api/src/federation/federation.module.ts (service registration) - apps/orchestrator/src/api/agents/agents.controller.ts (status endpoint) - apps/orchestrator/src/api/agents/agents.module.ts (lifecycle integration) Testing: - 12/12 tests passing for FederationAgentService - All command service tests passing - TypeScript compilation successful - Linting passed Refs #93 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
316 lines
8.2 KiB
Markdown
316 lines
8.2 KiB
Markdown
# M2 Multi-Tenant Database Layer - Implementation Summary
|
|
|
|
**Milestone:** M2 Multi-Tenant
|
|
**Issues:** #9 (Team Model), #10 (Row-Level Security)
|
|
**Date:** 2026-01-29
|
|
**Status:** ✅ Complete
|
|
|
|
## What Was Implemented
|
|
|
|
### 1. Team Model (#9)
|
|
|
|
Added comprehensive team support for workspace collaboration:
|
|
|
|
#### Schema Changes
|
|
|
|
**New Enum:**
|
|
|
|
```prisma
|
|
enum TeamMemberRole {
|
|
OWNER
|
|
ADMIN
|
|
MEMBER
|
|
}
|
|
```
|
|
|
|
**New Models:**
|
|
|
|
```prisma
|
|
model Team {
|
|
id String @id @default(uuid())
|
|
workspaceId String @map("workspace_id")
|
|
name String
|
|
description String?
|
|
metadata Json
|
|
// ... relations to Workspace and TeamMember
|
|
}
|
|
|
|
model TeamMember {
|
|
teamId String
|
|
userId String
|
|
role TeamMemberRole @default(MEMBER)
|
|
joinedAt DateTime
|
|
// ... relations to Team and User
|
|
}
|
|
```
|
|
|
|
**Updated Relations:**
|
|
|
|
- `User.teamMemberships` - Access user's team memberships
|
|
- `Workspace.teams` - Access workspace's teams
|
|
|
|
#### Database Tables Created
|
|
|
|
- `teams` - Stores team information within workspaces
|
|
- `team_members` - Join table for user-team relationships with roles
|
|
|
|
### 2. Row-Level Security (#10)
|
|
|
|
Implemented comprehensive RLS policies for complete tenant isolation:
|
|
|
|
#### RLS-Enabled Tables (19 total)
|
|
|
|
All tenant-scoped tables now have RLS enabled:
|
|
|
|
- Core: `workspaces`, `workspace_members`, `teams`, `team_members`
|
|
- Data: `tasks`, `events`, `projects`, `activity_logs`
|
|
- Features: `domains`, `ideas`, `relationships`, `agents`, `agent_sessions`
|
|
- UI: `user_layouts`
|
|
- Knowledge: `knowledge_entries`, `knowledge_tags`, `knowledge_entry_tags`, `knowledge_links`, `knowledge_embeddings`, `knowledge_entry_versions`
|
|
|
|
#### Helper Functions
|
|
|
|
Three utility functions for policy evaluation:
|
|
|
|
1. **`current_user_id()`** - Retrieves UUID from `app.current_user_id` session variable
|
|
2. **`is_workspace_member(workspace_uuid, user_uuid)`** - Checks workspace membership
|
|
3. **`is_workspace_admin(workspace_uuid, user_uuid)`** - Checks admin access (OWNER/ADMIN roles)
|
|
|
|
#### Policy Pattern
|
|
|
|
Consistent policy implementation across all tables:
|
|
|
|
```sql
|
|
CREATE POLICY <table>_workspace_access ON <table>
|
|
FOR ALL
|
|
USING (is_workspace_member(workspace_id, current_user_id()));
|
|
```
|
|
|
|
### 3. Developer Utilities
|
|
|
|
Created helper utilities for easy RLS integration in the API layer:
|
|
|
|
**File:** `apps/api/src/lib/db-context.ts`
|
|
|
|
**Key Functions:**
|
|
|
|
- `setCurrentUser(userId)` - Set user context for RLS
|
|
- `withUserContext(userId, fn)` - Execute function with user context
|
|
- `withUserTransaction(userId, fn)` - Transaction with user context
|
|
- `withAuth(handler)` - HOF for auto user context in handlers
|
|
- `verifyWorkspaceAccess(userId, workspaceId)` - Verify access
|
|
- `getUserWorkspaces(userId)` - Get user's workspaces
|
|
- `isWorkspaceAdmin(userId, workspaceId)` - Check admin access
|
|
- `createAuthMiddleware()` - tRPC middleware factory
|
|
|
|
## Files Created/Modified
|
|
|
|
### Schema & Migrations
|
|
|
|
- ✅ `apps/api/prisma/schema.prisma` - Added Team/TeamMember models
|
|
- ✅ `apps/api/prisma/migrations/20260129220941_add_team_model/` - Team model migration
|
|
- ✅ `apps/api/prisma/migrations/20260129221004_add_rls_policies/` - RLS policies migration
|
|
|
|
### Documentation
|
|
|
|
- ✅ `docs/design/multi-tenant-rls.md` - Comprehensive RLS documentation
|
|
- ✅ `docs/design/IMPLEMENTATION-M2-DATABASE.md` - This summary
|
|
|
|
### Utilities
|
|
|
|
- ✅ `apps/api/src/lib/db-context.ts` - RLS helper utilities
|
|
|
|
## How to Use
|
|
|
|
### In API Routes/Procedures
|
|
|
|
```typescript
|
|
import { withUserContext } from "@/lib/db-context";
|
|
|
|
// Method 1: Explicit context
|
|
export async function getTasks(userId: string, workspaceId: string) {
|
|
return withUserContext(userId, async () => {
|
|
return prisma.task.findMany({
|
|
where: { workspaceId },
|
|
});
|
|
});
|
|
}
|
|
|
|
// Method 2: HOF wrapper
|
|
import { withAuth } from "@/lib/db-context";
|
|
|
|
export const getTasks = withAuth(async ({ ctx, input }) => {
|
|
return prisma.task.findMany({
|
|
where: { workspaceId: input.workspaceId },
|
|
});
|
|
});
|
|
|
|
// Method 3: Transaction
|
|
import { withUserTransaction } from "@/lib/db-context";
|
|
|
|
export async function createWorkspace(userId: string, name: string) {
|
|
return withUserTransaction(userId, async (tx) => {
|
|
const workspace = await tx.workspace.create({
|
|
data: { name, ownerId: userId },
|
|
});
|
|
|
|
await tx.workspaceMember.create({
|
|
data: { workspaceId: workspace.id, userId, role: "OWNER" },
|
|
});
|
|
|
|
return workspace;
|
|
});
|
|
}
|
|
```
|
|
|
|
### Testing RLS
|
|
|
|
```sql
|
|
-- Manual testing in psql
|
|
SET app.current_user_id = 'user-uuid-here';
|
|
|
|
-- Should only see authorized data
|
|
SELECT * FROM tasks;
|
|
|
|
-- Should be empty for unauthorized workspace
|
|
SELECT * FROM tasks WHERE workspace_id = 'other-workspace-uuid';
|
|
```
|
|
|
|
## Verification Checklist
|
|
|
|
- ✅ Team model added to schema
|
|
- ✅ TeamMember model added with roles
|
|
- ✅ All tenant-scoped models have `workspaceId` foreign key
|
|
- ✅ RLS enabled on all tenant-scoped tables
|
|
- ✅ RLS policies created for all tables
|
|
- ✅ Helper functions implemented
|
|
- ✅ Developer utilities created
|
|
- ✅ Comprehensive documentation written
|
|
- ✅ Migrations applied successfully
|
|
- ✅ Prisma client regenerated
|
|
|
|
## Security Notes
|
|
|
|
### Defense in Depth
|
|
|
|
RLS provides **database-level security** but is part of a layered approach:
|
|
|
|
1. **Authentication** - Verify user identity
|
|
2. **Application validation** - Check permissions in API
|
|
3. **RLS policies** - Enforce at database level (failsafe)
|
|
|
|
### Important Reminders
|
|
|
|
⚠️ **Always set `app.current_user_id` before queries**
|
|
⚠️ **RLS does not replace application logic**
|
|
⚠️ **Test with different user roles**
|
|
⚠️ **Use `withoutRLS()` only for system operations**
|
|
|
|
## Performance Considerations
|
|
|
|
- ✅ All tables have indexes on `workspaceId`
|
|
- ✅ Helper functions marked as `STABLE` for caching
|
|
- ✅ Policies use indexed columns for filtering
|
|
- ✅ Functions use `SECURITY DEFINER` for consistent execution
|
|
|
|
## Next Steps
|
|
|
|
### Immediate (Required)
|
|
|
|
1. **Update API routes** - Add `withUserContext` to all routes
|
|
2. **Add middleware** - Use `createAuthMiddleware()` in tRPC
|
|
3. **Test access control** - Verify RLS with multiple users
|
|
4. **Update frontend** - Handle workspace selection
|
|
|
|
### Future Enhancements (Optional)
|
|
|
|
1. **Team-level permissions** - Extend RLS for team-specific data
|
|
2. **Project-level isolation** - Add policies for project sharing
|
|
3. **Audit logging** - Track all data access via RLS
|
|
4. **Fine-grained RBAC** - Extend beyond workspace roles
|
|
|
|
## Testing Instructions
|
|
|
|
### 1. Verify Migrations
|
|
|
|
```bash
|
|
cd apps/api
|
|
npx prisma migrate status
|
|
# Should show: Database schema is up to date!
|
|
```
|
|
|
|
### 2. Test RLS in Database
|
|
|
|
```sql
|
|
-- Connect to database
|
|
psql mosaic
|
|
|
|
-- Create test users and workspaces (if not exist)
|
|
-- ...
|
|
|
|
-- Test isolation
|
|
SET app.current_user_id = 'user-1-uuid';
|
|
SELECT * FROM workspaces; -- Should only see user 1's workspaces
|
|
|
|
SET app.current_user_id = 'user-2-uuid';
|
|
SELECT * FROM workspaces; -- Should only see user 2's workspaces
|
|
```
|
|
|
|
### 3. Test API Utilities
|
|
|
|
```typescript
|
|
// In a test file
|
|
import { withUserContext, verifyWorkspaceAccess } from "@/lib/db-context";
|
|
|
|
describe("RLS Utilities", () => {
|
|
it("should isolate workspaces", async () => {
|
|
const workspaces = await withUserContext(user1Id, async () => {
|
|
return prisma.workspace.findMany();
|
|
});
|
|
|
|
expect(workspaces.every((w) => w.members.some((m) => m.userId === user1Id))).toBe(true);
|
|
});
|
|
|
|
it("should verify access", async () => {
|
|
const hasAccess = await verifyWorkspaceAccess(userId, workspaceId);
|
|
expect(hasAccess).toBe(true);
|
|
});
|
|
});
|
|
```
|
|
|
|
## References
|
|
|
|
- Issue #9: Multi-tenant setup — workspace/team models
|
|
- Issue #10: Row-Level Security for data isolation
|
|
- Documentation: `docs/design/multi-tenant-rls.md`
|
|
- Utilities: `apps/api/src/lib/db-context.ts`
|
|
|
|
## Migration Commands Used
|
|
|
|
```bash
|
|
# Format schema
|
|
cd apps/api && npx prisma format
|
|
|
|
# Create Team model migration
|
|
npx prisma migrate dev --name add_team_model --create-only
|
|
|
|
# Create RLS migration
|
|
npx prisma migrate dev --name add_rls_policies --create-only
|
|
|
|
# Apply migrations
|
|
npx prisma migrate deploy
|
|
|
|
# Regenerate client
|
|
npx prisma generate
|
|
```
|
|
|
|
## Summary
|
|
|
|
✅ **Complete tenant isolation at database level**
|
|
✅ **Team collaboration within workspaces**
|
|
✅ **Developer-friendly utilities**
|
|
✅ **Comprehensive documentation**
|
|
✅ **Production-ready security**
|
|
|
|
The multi-tenant database foundation is now complete and ready for application integration!
|