UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

141 lines 7.19 kB
/** * ADR-095 G2 — pluggable transport for hive-mind consensus protocols. * * The raft/byzantine/gossip consensus implementations historically used a * local `EventEmitter` for *everything* — both observability events * ("leader.elected", "consensus.achieved") AND inter-node messages * (append-entries, vote requests, pre-prepare/prepare/commit). The latter * never actually crossed a process or node boundary: a node "sent" a * message by `emit`ting it locally and synthesizing the peer's reply * inline. That's the single-process limitation #G2 names. * * This module separates the inter-node-message dimension behind a * `ConsensusTransport` interface. Two implementations: * * - `LocalTransport` — an in-process registry. Multiple consensus * instances in the same Node process share a registry and deliver * messages to each other synchronously. Matches the current * single-process behavior; the default so nothing breaks. * - `FederationTransport` (separate file, ADR-104 wire) — serializes * ConsensusMessages into federation envelopes, signs them with the * node's Ed25519 key, sends over WS via agentic-flow/transport/loader, * and dispatches inbound envelopes with signature verification. * * Observability events stay on the consensus class's own EventEmitter — * this is purely the messaging layer. * * No new dependencies: Ed25519 signing uses Node's built-in `crypto` * (`generateKeyPairSync('ed25519')` + `sign`/`verify` with `null` algorithm, * which is correct for Ed25519). */ /** A message exchanged between consensus nodes. */ export interface ConsensusMessage { /** Protocol message type — e.g. 'append-entries', 'request-vote', 'pre-prepare', 'prepare', 'commit', 'gossip', 'gossip-ack'. */ readonly type: string; /** Sender node id. */ readonly from: string; /** Recipient node id. Omit for broadcast. */ readonly to?: string; /** Protocol payload (term, log entries, vote, digest, …). */ readonly payload: unknown; /** Raft term, when applicable. Lets the transport drop stale-term messages cheaply. */ readonly term?: number; /** PBFT view number, when applicable. */ readonly viewNumber?: number; /** Monotonic per-sender sequence number — replay defense. */ readonly seq?: number; /** Ed25519 signature (base64) over `canonicalizeForSigning(msg)`. */ readonly signature?: string; } /** * A reply to a `send()`. Protocols use this for the request-response legs * (request-vote → vote-response, append-entries → append-entries-response). * Broadcasts don't get replies; responses arrive via `onMessage`. */ export type ConsensusReply = ConsensusMessage | null; export type ConsensusMessageHandler = (msg: ConsensusMessage) => Promise<ConsensusReply | void> | ConsensusReply | void; export interface ConsensusTransport { /** This node's id (the one consensus protocols use as `from`). */ readonly nodeId: string; /** * Send a message to a specific peer. Resolves with the peer's reply * (or `null` if the peer ack'd without a reply), rejects on timeout or * unreachable peer. `timeoutMs` defaults to the transport's configured value. */ send(to: string, msg: Omit<ConsensusMessage, 'from'>, timeoutMs?: number): Promise<ConsensusReply>; /** Broadcast to all currently-reachable peers. Resolves once dispatched; replies (if any) arrive via onMessage. */ broadcast(msg: Omit<ConsensusMessage, 'from'>): Promise<void>; /** Register the inbound-message handler. Calling again replaces the previous handler. */ onMessage(handler: ConsensusMessageHandler): void; /** Currently-reachable peer node ids (excludes self). */ peers(): readonly string[]; /** Tear down. After close(), send/broadcast reject. */ close(): Promise<void>; } export interface NodeKeyPair { /** Ed25519 private key in PKCS8 PEM. */ readonly privateKeyPem: string; /** Ed25519 public key in SPKI PEM. */ readonly publicKeyPem: string; } /** Generate a fresh Ed25519 keypair for a consensus node. */ export declare function generateNodeKeyPair(): NodeKeyPair; /** * Canonical byte string for signing. Deterministic across hosts: deep-sorted-key * JSON of the message's content fields (everything except `signature`). */ export declare function canonicalizeForSigning(msg: Omit<ConsensusMessage, 'signature'>): Buffer; /** Stable digest of a message's content — handy for dedup and logging. */ export declare function messageDigest(msg: Omit<ConsensusMessage, 'signature'>): string; /** Sign a message with an Ed25519 private key (PEM). Returns base64 signature. */ export declare function signMessage(msg: Omit<ConsensusMessage, 'signature'>, privateKeyPem: string): string; /** * Verify a signed message against a peer's Ed25519 public key (PEM). * Returns true iff the signature is present and valid over the message's * content fields. Fail-closed: a missing signature returns false. */ export declare function verifyMessage(msg: ConsensusMessage, publicKeyPem: string): boolean; /** * Shared registry of LocalTransport instances. Multiple consensus nodes in * the same process register here; send/broadcast deliver to peers' handlers. * Use a fresh registry per test to keep tests isolated. */ export declare class LocalTransportRegistry { private readonly nodes; register(t: LocalTransport): void; unregister(nodeId: string): void; get(nodeId: string): LocalTransport | undefined; peerIds(exclude: string): string[]; } /** Process-wide default registry. Tests should pass their own. */ export declare const defaultLocalRegistry: LocalTransportRegistry; export interface LocalTransportOptions { readonly registry?: LocalTransportRegistry; readonly defaultTimeoutMs?: number; /** Optional Ed25519 keypair — when set, outbound messages are signed and inbound are verified against the sender's pubkey (resolved via `resolvePeerPublicKey`). */ readonly keyPair?: NodeKeyPair; /** Map a peer nodeId → its Ed25519 public key PEM. Required if `keyPair` is set and you want verification. */ readonly resolvePeerPublicKey?: (nodeId: string) => string | undefined; } export declare class LocalTransport implements ConsensusTransport { readonly nodeId: string; private readonly registry; private readonly defaultTimeoutMs; private readonly keyPair?; private readonly resolvePeerPublicKey?; private handler; private closed; private seqCounter; /** Per-sender last-seen seq for replay defense (only used when signed). */ private readonly lastSeenSeq; constructor(nodeId: string, opts?: LocalTransportOptions); onMessage(handler: ConsensusMessageHandler): void; peers(): readonly string[]; private stamp; /** Deliver an inbound message to a target's handler, with optional sig + replay checks. */ private deliver; send(to: string, msg: Omit<ConsensusMessage, 'from'>, timeoutMs?: number): Promise<ConsensusReply>; broadcast(msg: Omit<ConsensusMessage, 'from'>): Promise<void>; close(): Promise<void>; } //# sourceMappingURL=transport.d.ts.map