feat(#84): implement instance identity model for federation

Implemented the foundation of federation architecture with instance
identity and connection management:

Database Schema:
- Added Instance model for instance identity with keypair generation
- Added FederationConnection model for workspace-scoped connections
- Added FederationConnectionStatus enum (PENDING, ACTIVE, SUSPENDED, DISCONNECTED)

Service Layer:
- FederationService with instance identity management
- RSA 2048-bit keypair generation for signing
- Public identity endpoint (excludes private key)
- Keypair regeneration capability

API Endpoints:
- GET /api/v1/federation/instance - Returns public instance identity
- POST /api/v1/federation/instance/regenerate-keys - Admin keypair regeneration

Tests:
- 11 tests passing (7 service, 4 controller)
- 100% statement coverage, 100% function coverage
- Follows TDD principles (Red-Green-Refactor)

Configuration:
- Added INSTANCE_NAME and INSTANCE_URL environment variables
- Integrated FederationModule into AppModule

Refs #84

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-03 10:58:50 -06:00
parent 6e63508f97
commit 7989c089ef
10 changed files with 853 additions and 22 deletions

View File

@@ -166,6 +166,13 @@ enum JobStepStatus {
SKIPPED
}
enum FederationConnectionStatus {
PENDING
ACTIVE
SUSPENDED
DISCONNECTED
}
// ============================================
// MODELS
// ============================================
@@ -226,28 +233,29 @@ model Workspace {
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
// Relations
owner User @relation("WorkspaceOwner", fields: [ownerId], references: [id], onDelete: Cascade)
members WorkspaceMember[]
teams Team[]
tasks Task[]
events Event[]
projects Project[]
activityLogs ActivityLog[]
memoryEmbeddings MemoryEmbedding[]
domains Domain[]
ideas Idea[]
relationships Relationship[]
agents Agent[]
agentSessions AgentSession[]
agentTasks AgentTask[]
userLayouts UserLayout[]
knowledgeEntries KnowledgeEntry[]
knowledgeTags KnowledgeTag[]
cronSchedules CronSchedule[]
personalities Personality[]
llmSettings WorkspaceLlmSettings?
qualityGates QualityGate[]
runnerJobs RunnerJob[]
owner User @relation("WorkspaceOwner", fields: [ownerId], references: [id], onDelete: Cascade)
members WorkspaceMember[]
teams Team[]
tasks Task[]
events Event[]
projects Project[]
activityLogs ActivityLog[]
memoryEmbeddings MemoryEmbedding[]
domains Domain[]
ideas Idea[]
relationships Relationship[]
agents Agent[]
agentSessions AgentSession[]
agentTasks AgentTask[]
userLayouts UserLayout[]
knowledgeEntries KnowledgeEntry[]
knowledgeTags KnowledgeTag[]
cronSchedules CronSchedule[]
personalities Personality[]
llmSettings WorkspaceLlmSettings?
qualityGates QualityGate[]
runnerJobs RunnerJob[]
federationConnections FederationConnection[]
@@index([ownerId])
@@map("workspaces")
@@ -1217,3 +1225,58 @@ model JobEvent {
@@index([jobId, timestamp])
@@map("job_events")
}
// ============================================
// FEDERATION MODULE
// ============================================
model Instance {
id String @id @default(uuid()) @db.Uuid
instanceId String @unique @map("instance_id") // Unique identifier for federation
name String
url String
publicKey String @map("public_key") @db.Text
privateKey String @map("private_key") @db.Text // Encrypted private key
// Capabilities and metadata
capabilities Json @default("{}")
metadata Json @default("{}")
// Timestamps
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
@@map("instances")
}
model FederationConnection {
id String @id @default(uuid()) @db.Uuid
workspaceId String @map("workspace_id") @db.Uuid
// Remote instance details
remoteInstanceId String @map("remote_instance_id")
remoteUrl String @map("remote_url")
remotePublicKey String @map("remote_public_key") @db.Text
remoteCapabilities Json @default("{}") @map("remote_capabilities")
// Connection status
status FederationConnectionStatus @default(PENDING)
// Metadata
metadata Json @default("{}")
// Timestamps
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
connectedAt DateTime? @map("connected_at") @db.Timestamptz
disconnectedAt DateTime? @map("disconnected_at") @db.Timestamptz
// Relations
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
@@unique([workspaceId, remoteInstanceId])
@@index([workspaceId])
@@index([workspaceId, status])
@@index([remoteInstanceId])
@@map("federation_connections")
}