import { FederationNode, type FederationNodeStateRecord } from '../domain/entities/federation-node.js'; import { FederationSession } from '../domain/entities/federation-session.js'; import { type FederationMessageType } from '../domain/entities/federation-envelope.js'; import { TrustLevel } from '../domain/entities/trust-level.js'; import { type SuspensionReason } from '../domain/value-objects/federation-node-state.js'; import { DiscoveryService, type FederationManifest } from '../domain/services/discovery-service.js'; import { HandshakeService } from '../domain/services/handshake-service.js'; import { RoutingService, type RoutingResult } from '../domain/services/routing-service.js'; import { AuditService } from '../domain/services/audit-service.js'; import { PIIPipelineService } from '../domain/services/pii-pipeline-service.js'; import { TrustEvaluator, type BootstrapElevationAuditEntry } from './trust-evaluator.js'; import { PolicyEngine } from './policy-engine.js'; import { enforceBudget, validateBudget } from '../domain/value-objects/federation-budget.js'; import type { FederationBreakerService } from './federation-breaker-service.js'; import type { SpendReporter } from './spend-reporter.js'; import type { WgMeshService, WgCommand } from '../domain/services/wg-mesh-service.js'; /** * Optional per-call budget controls (ADR-097 Phase 1). All fields are * optional; omitted means "no limit on that axis" (the default unbounded * budget still has maxHops = 8 to defang recursive delegation loops). */ export interface SendOptions { readonly budget?: { maxTokens?: number; maxUsd?: number; }; readonly maxHops?: number; /** * How many hops this message has already traveled (0 on the originator). * Phase 1 enforces at the send side because there is no inbound dispatcher * yet; callers re-forwarding a received message should pass the original * envelope's hopCount here. */ readonly hopCount?: number; /** Caller-reported usage from the previous leg, for cumulative checks. */ readonly spent?: { tokens?: number; usd?: number; }; } export interface FederationCoordinatorConfig { readonly nodeId: string; readonly publicKey: string; readonly endpoint: string; readonly capabilities: readonly string[]; /** Wire protocols this runtime is prepared to advertise and negotiate. */ readonly supportedProtocols?: readonly string[]; readonly version?: string; } export interface FederationStatus { readonly nodeId: string; readonly activeSessions: number; readonly knownPeers: number; readonly trustLevels: Record; readonly healthy: boolean; } /** * Optional integrations (ADR-097 Phase 3 upstream + Phase 2.b breaker * wiring). Both are constructor-injected; both default to no-op. * * - `spendReporter` — invoked by reportSpend() when present; persists * the FederationSpendEvent to whatever backend the integrator wired * (cost-tracker bus, ruflo memory federation-spend namespace, etc.) * - `breakerService` — invoked by reportSpend() when present; calls * recordOutcome() so the breaker's in-memory rolling buffer is fed * without requiring the integrator to wire two parallel pipelines */ export interface FederationCoordinatorIntegrations { readonly spendReporter?: SpendReporter; readonly breakerService?: FederationBreakerService; /** * ADR-111 Phase 3 — optional WG mesh service. When present, peer state * transitions (evict/reactivate/breaker-suspend) emit `wg set` commands * to a sink supplied by `wgCommandSink`. No-op for peers without a `wg` * manifest block, so wiring the service is safe even in mixed deployments. */ readonly wgMesh?: WgMeshService; /** * Where emitted WG commands go. Defaults to the audit log only. Integrators * can plug in a shell executor here (after operator approval — bringing up * a network interface is destructive). Commands include shell-ready * `wg set ...` strings; the sink should still validate before exec. */ readonly wgCommandSink?: (cmd: WgCommand) => void | Promise; } export declare class FederationCoordinator { private readonly config; private readonly discovery; private readonly handshake; private readonly routing; private readonly audit; private readonly piiPipeline; private readonly trustEvaluator; private readonly policyEngine; private readonly sessions; private initialized; private readonly spendReporter?; private readonly breakerService?; private readonly wgMesh?; private readonly wgCommandSink?; constructor(config: FederationCoordinatorConfig, discovery: DiscoveryService, handshake: HandshakeService, routing: RoutingService, audit: AuditService, piiPipeline: PIIPipelineService, trustEvaluator: TrustEvaluator, policyEngine: PolicyEngine, integrations?: FederationCoordinatorIntegrations); /** * ADR-111 Phase 3 — emit a WG command for an audited peer transition. * No-op if no wgMesh is configured or the peer lacks a `wg` manifest * block. Commands flow to `wgCommandSink` (if any) and always to the * audit log so reactivation is fully traceable. */ private emitWgCommand; initialize(manifest: Omit): Promise; shutdown(): Promise; joinPeer(endpoint: string, manifest?: FederationManifest): Promise; leavePeer(nodeId: string): Promise; sendMessage(targetNodeId: string, messageType: FederationMessageType, payload: T, options?: SendOptions): Promise; /** * Re-export the budget primitives so external callers (e.g. follow-up * iterations adding receive-side decrement) can reuse them without * dipping into domain/value-objects directly. */ static readonly budget: { validate: typeof validateBudget; enforce: typeof enforceBudget; }; broadcastMessage(messageType: FederationMessageType, payload: T): Promise; handleThreatDetection(nodeId: string): void; handleHmacFailure(nodeId: string): void; getStatus(): FederationStatus; /** * ADR-097 Phase 4: per-peer breaker state snapshot for the doctor * surface and `federation_breaker_status` MCP tool. * * Returns one entry per known peer with the entity's stateRecord — * what state, when it changed, why, and which caller's correlation * key triggered it. Pure read; does not mutate. */ getPeerStates(): readonly (FederationNodeStateRecord & { readonly nodeId: string; })[]; /** * ADR-111 Phase 6: public read of the discovery peer list. Used by * federation_wg_status MCP tool + WG firewall projection. Returns the * full known-peer set (active + suspended + evicted) — filter via * .isActive / state if you only want the live mesh members. */ listPeers(): readonly FederationNode[]; /** * Aggregated counts for the doctor surface — `{ active: N, suspended: M, * evicted: K }`. Cheap O(peers) sweep; safe to call from a status line. */ getPeerStateCounts(): { readonly active: number; readonly suspended: number; readonly evicted: number; }; /** * Operator-initiated evict. Returns true on transition, false if the * peer was already EVICTED or unknown. Logs to audit either way. * * Does NOT remove the peer from the discovery registry — `leavePeer` * is the registry-removal API. Eviction is the breaker layer; the peer * remains queryable so the operator can later reactivate. */ evictPeer(nodeId: string, reason?: SuspensionReason, correlationId?: string): Promise; /** * ADR-097 Phase 3 upstream: report the actual cost of a federated * call. Federation doesn't own model pricing, so the integrator calls * this after the downstream agent completes. * * Fans out to: * - spendReporter (if injected) — persists to integrator's backend * - breakerService.recordOutcome (if injected) — feeds the breaker's * in-memory rolling buffer for cost/failure-ratio thresholds * * Both are no-ops if the corresponding integration isn't wired, so * callers don't need to branch on configuration. Negative tokens/usd * are clamped to 0 at the breaker layer (anti-credit-inflation); the * spend reporter receives the raw values so backends can audit them. * * Auto-fills `ts` if the caller omits it. */ reportSpend(input: { readonly peerId: string; readonly taskId?: string; readonly tokensUsed: number; readonly usdSpent: number; readonly success: boolean; readonly ts?: string; }): Promise; /** * Founder-bootstrap trust elevation (ADR-164 §3.5.4 — operator escape hatch * used in the autopilot Day-1 scenario where a freshly-joined BBS peer * needs TRUSTED before organic minInteractions thresholds can accrue). * * Refuses if the target node is not a registered federation peer (returns * null). On success, writes a `trust_level_changed` audit entry tagged * `bootstrap_elevation` with the operator-supplied reason, and returns * the audit entry so the CLI / caller can print it to stdout. */ bootstrapElevatePeer(nodeId: string, newLevel: TrustLevel, reason: string): Promise; /** * Operator-initiated reactivate. Used after an integrator-supplied * health probe confirms a SUSPENDED peer is healthy, OR as an * operator-override escape from EVICTED. Returns true on transition. */ reactivatePeer(nodeId: string, correlationId?: string): Promise; getSession(sessionId: string): FederationSession | undefined; getActiveSessions(): FederationSession[]; private establishSession; private findSessionByNodeId; private ensureInitialized; } //# sourceMappingURL=federation-coordinator.d.ts.map