# Issue #85: [FED-002] CONNECT/DISCONNECT Protocol ## Objective Implement the connection handshake protocol for federation, building on the Instance Identity Model from issue #84. This includes: - Connection request/accept/reject handshake - Message signing and verification using instance keypairs - Connection state management (PENDING → ACTIVE, DISCONNECTED) - API endpoints for initiating and managing connections - Proper error handling and validation ## Context Issue #84 provides the foundation: - `Instance` model with keypair for signing - `FederationConnection` model with status enum (PENDING, ACTIVE, SUSPENDED, DISCONNECTED) - `FederationService` with identity management - `CryptoService` for encryption/decryption - Database schema is already in place ## Approach ### 1. Create Types for Connection Protocol Define TypeScript interfaces in `/apps/api/src/federation/types/connection.types.ts`: ```typescript // Connection request payload interface ConnectionRequest { instanceId: string; instanceUrl: string; publicKey: string; capabilities: FederationCapabilities; timestamp: number; signature: string; // Sign entire payload with private key } // Connection response interface ConnectionResponse { accepted: boolean; instanceId: string; publicKey: string; capabilities: FederationCapabilities; reason?: string; // If rejected timestamp: number; signature: string; } // Disconnect request interface DisconnectRequest { instanceId: string; reason?: string; timestamp: number; signature: string; } ``` ### 2. Add Signature Service Create `/apps/api/src/federation/signature.service.ts` for message signing: - `sign(message: object, privateKey: string): string` - Sign a message - `verify(message: object, signature: string, publicKey: string): boolean` - Verify signature - `signConnectionRequest(...)` - Sign connection request - `verifyConnectionRequest(...)` - Verify connection request ### 3. Create Connection Service Create `/apps/api/src/federation/connection.service.ts`: - `initiateConnection(workspaceId, remoteUrl)` - Start connection handshake - `acceptConnection(workspaceId, connectionId)` - Accept pending connection - `rejectConnection(workspaceId, connectionId, reason)` - Reject connection - `disconnect(workspaceId, connectionId, reason)` - Disconnect active connection - `getConnections(workspaceId, status?)` - List connections - `getConnection(workspaceId, connectionId)` - Get single connection ### 4. Add API Endpoints Extend `FederationController` with: - `POST /api/v1/federation/connections/initiate` - Initiate connection to remote instance - `POST /api/v1/federation/connections/:id/accept` - Accept incoming connection - `POST /api/v1/federation/connections/:id/reject` - Reject incoming connection - `POST /api/v1/federation/connections/:id/disconnect` - Disconnect active connection - `GET /api/v1/federation/connections` - List workspace connections - `GET /api/v1/federation/connections/:id` - Get connection details - `POST /api/v1/federation/incoming/connect` - Public endpoint for receiving connection requests ### 5. Connection Handshake Flow **Initiator (Instance A) → Target (Instance B)** 1. Instance A calls `POST /api/v1/federation/connections/initiate` with `remoteUrl` 2. Service creates connection record with status=PENDING 3. Service fetches remote instance identity from `GET {remoteUrl}/api/v1/federation/instance` 4. Service creates signed ConnectionRequest 5. Service sends request to `POST {remoteUrl}/api/v1/federation/incoming/connect` 6. Instance B receives request, validates signature 7. Instance B creates connection record with status=PENDING 8. Instance B can accept (status=ACTIVE) or reject (status=DISCONNECTED) 9. Instance B sends signed ConnectionResponse back to Instance A 10. Instance A updates connection status based on response ### 6. Security Considerations - All connection requests must be signed with instance private key - All responses must be verified using remote instance public key - Timestamps must be within 5 minutes to prevent replay attacks - Connection requests must come from authenticated workspace members - Public key must match the one fetched from remote instance identity endpoint ### 7. Testing Strategy **Unit Tests** (TDD approach): - SignatureService.sign() creates valid signatures - SignatureService.verify() validates signatures correctly - SignatureService.verify() rejects invalid signatures - ConnectionService.initiateConnection() creates PENDING connection - ConnectionService.acceptConnection() updates to ACTIVE - ConnectionService.rejectConnection() marks as DISCONNECTED - ConnectionService.disconnect() updates active connection to DISCONNECTED - Timestamp validation rejects old requests (>5 min) **Integration Tests**: - POST /connections/initiate creates connection and calls remote - POST /incoming/connect validates signature and creates connection - POST /connections/:id/accept updates status correctly - POST /connections/:id/reject marks connection as rejected - POST /connections/:id/disconnect disconnects active connection - GET /connections returns workspace connections - Workspace isolation (can't access other workspace connections) ## Progress - [x] Create scratchpad - [x] Create connection.types.ts with protocol types - [x] Write tests for SignatureService (18 tests) - [x] Implement SignatureService (sign, verify, validateTimestamp) - [x] Write tests for ConnectionService (20 tests) - [x] Implement ConnectionService (all 8 methods) - [x] Write tests for connection API endpoints (13 tests) - [x] Implement connection API endpoints (7 endpoints) - [x] Update FederationModule with new providers - [x] Verify all tests pass (70/70 passing) - [x] Verify type checking passes - [x] Verify test coverage ≥85% (100% coverage on new code) - [x] Commit changes (commit fc39190) ## Testing Plan ### Unit Tests 1. **SignatureService**: - Should create RSA SHA-256 signatures - Should verify valid signatures - Should reject invalid signatures - Should reject tampered messages - Should reject expired timestamps 2. **ConnectionService**: - Should initiate connection with PENDING status - Should fetch remote instance identity before connecting - Should create signed connection request - Should accept connection and update to ACTIVE - Should reject connection with reason - Should disconnect active connection - Should list connections for workspace - Should enforce workspace isolation ### Integration Tests 1. **POST /api/v1/federation/connections/initiate**: - Should require authentication - Should create connection record - Should fetch remote instance identity - Should return connection details 2. **POST /api/v1/federation/incoming/connect**: - Should validate connection request signature - Should reject requests with invalid signatures - Should reject requests with old timestamps - Should create pending connection 3. **POST /api/v1/federation/connections/:id/accept**: - Should require authentication - Should update connection to ACTIVE - Should set connectedAt timestamp - Should enforce workspace ownership 4. **POST /api/v1/federation/connections/:id/reject**: - Should require authentication - Should update connection to DISCONNECTED - Should store rejection reason 5. **POST /api/v1/federation/connections/:id/disconnect**: - Should require authentication - Should disconnect active connection - Should set disconnectedAt timestamp 6. **GET /api/v1/federation/connections**: - Should list workspace connections - Should filter by status if provided - Should enforce workspace isolation ## Design Decisions 1. **RSA Signatures**: Use RSA SHA-256 for signing (matches existing keypair format) 2. **Timestamp Validation**: 5-minute window to prevent replay attacks 3. **Workspace Scoping**: All connections belong to a workspace for RLS 4. **Stateless Protocol**: Each request is independently signed and verified 5. **Public Connection Endpoint**: `/incoming/connect` is public (no auth) but requires valid signature 6. **State Transitions**: PENDING → ACTIVE, PENDING → DISCONNECTED, ACTIVE → DISCONNECTED ## Notes - Connection requests are workspace-scoped (authenticated users only) - Incoming connection endpoint is public but cryptographically verified - Need to handle network errors gracefully when calling remote instances - Should validate remote instance URL format before attempting connection - Consider rate limiting for incoming connection requests (future enhancement)