/** * Federation API Client * Handles federation connection management API requests */ import { apiGet, apiPost, apiPatch } from "./client"; /** * Federation connection status */ export enum FederationConnectionStatus { PENDING = "PENDING", ACTIVE = "ACTIVE", DISCONNECTED = "DISCONNECTED", REJECTED = "REJECTED", } /** * Federation capabilities */ export interface FederationCapabilities { supportsQuery: boolean; supportsCommand: boolean; supportsEvent: boolean; supportsAgentSpawn: boolean; protocolVersion: string; } /** * Connection details */ export interface ConnectionDetails { id: string; workspaceId: string; remoteInstanceId: string; remoteUrl: string; remotePublicKey: string; remoteCapabilities: FederationCapabilities; status: FederationConnectionStatus; metadata: Record; createdAt: string; updatedAt: string; connectedAt: string | null; disconnectedAt: string | null; } /** * Public instance identity */ export interface PublicInstanceIdentity { id: string; instanceId: string; name: string; url: string; publicKey: string; capabilities: FederationCapabilities; metadata: Record; createdAt: string; updatedAt: string; } /** * Initiate connection request */ export interface InitiateConnectionRequest { remoteUrl: string; } /** * Accept connection request */ export interface AcceptConnectionRequest { metadata?: Record; } /** * Reject connection request */ export interface RejectConnectionRequest { reason: string; } /** * Disconnect connection request */ export interface DisconnectConnectionRequest { reason?: string; } /** * Fetch all connections */ export async function fetchConnections( status?: FederationConnectionStatus ): Promise { const params = new URLSearchParams(); if (status) { params.append("status", status); } const queryString = params.toString(); const endpoint = queryString ? `/api/v1/federation/connections?${queryString}` : "/api/v1/federation/connections"; return apiGet(endpoint); } /** * Fetch a single connection */ export async function fetchConnection(connectionId: string): Promise { return apiGet(`/api/v1/federation/connections/${connectionId}`); } /** * Initiate a new connection */ export async function initiateConnection( request: InitiateConnectionRequest ): Promise { return apiPost("/api/v1/federation/connections/initiate", request); } /** * Accept a pending connection */ export async function acceptConnection( connectionId: string, request?: AcceptConnectionRequest ): Promise { return apiPost( `/api/v1/federation/connections/${connectionId}/accept`, request ?? {} ); } /** * Reject a pending connection */ export async function rejectConnection( connectionId: string, request: RejectConnectionRequest ): Promise { return apiPost( `/api/v1/federation/connections/${connectionId}/reject`, request ); } /** * Disconnect an active connection */ export async function disconnectConnection( connectionId: string, request?: DisconnectConnectionRequest ): Promise { return apiPost( `/api/v1/federation/connections/${connectionId}/disconnect`, request ?? {} ); } /** * Get this instance's public identity */ export async function fetchInstanceIdentity(): Promise { return apiGet("/api/v1/federation/instance"); } /** * Update instance configuration request */ export interface UpdateInstanceRequest { name?: string; capabilities?: FederationCapabilities; metadata?: Record; } /** * Update this instance's configuration * Admin-only operation */ export async function updateInstanceConfiguration( updates: UpdateInstanceRequest ): Promise { return apiPatch("/api/v1/federation/instance", updates); } /** * Regenerate instance keypair * Admin-only operation */ export async function regenerateInstanceKeys(): Promise { return apiPost("/api/v1/federation/instance/regenerate-keys", {}); } /** * Get mock connections for development only. * Returns an empty array in production as defense-in-depth. * The federation pages are also gated behind a ComingSoon component * in production (SEC-WEB-4), but this provides an additional layer. */ export function getMockConnections(): ConnectionDetails[] { if (process.env.NODE_ENV !== "development") { return []; } return [ { id: "conn-1", workspaceId: "workspace-1", remoteInstanceId: "instance-work-001", remoteUrl: "https://mosaic.work.example.com", remotePublicKey: "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----", remoteCapabilities: { supportsQuery: true, supportsCommand: true, supportsEvent: true, supportsAgentSpawn: true, protocolVersion: "1.0", }, status: FederationConnectionStatus.ACTIVE, metadata: { name: "Work Instance", description: "Corporate Mosaic instance", }, createdAt: new Date("2026-02-01").toISOString(), updatedAt: new Date("2026-02-01").toISOString(), connectedAt: new Date("2026-02-01").toISOString(), disconnectedAt: null, }, { id: "conn-2", workspaceId: "workspace-1", remoteInstanceId: "instance-partner-001", remoteUrl: "https://mosaic.partner.example.com", remotePublicKey: "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----", remoteCapabilities: { supportsQuery: true, supportsCommand: false, supportsEvent: true, supportsAgentSpawn: false, protocolVersion: "1.0", }, status: FederationConnectionStatus.PENDING, metadata: { name: "Partner Instance", description: "Shared project collaboration", }, createdAt: new Date("2026-02-02").toISOString(), updatedAt: new Date("2026-02-02").toISOString(), connectedAt: null, disconnectedAt: null, }, { id: "conn-3", workspaceId: "workspace-1", remoteInstanceId: "instance-old-001", remoteUrl: "https://mosaic.old.example.com", remotePublicKey: "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----", remoteCapabilities: { supportsQuery: true, supportsCommand: true, supportsEvent: false, supportsAgentSpawn: false, protocolVersion: "1.0", }, status: FederationConnectionStatus.DISCONNECTED, metadata: { name: "Previous Instance", description: "No longer in use", }, createdAt: new Date("2026-01-15").toISOString(), updatedAt: new Date("2026-01-30").toISOString(), connectedAt: new Date("2026-01-15").toISOString(), disconnectedAt: new Date("2026-01-30").toISOString(), }, ]; }