{"record":{"id":"0dca505cdcdec02b","repo":"ruvnet/ruflo","slug":"localtransport-signature-verification-failed-for","errorCode":null,"errorMessage":"LocalTransport: signature verification failed for message from ${msg.from}","messagePattern":"LocalTransport: signature verification failed for message from (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"v3/@claude-flow/swarm/src/consensus/transport.ts","lineNumber":242,"sourceCode":"    const base: Omit<ConsensusMessage, 'signature'> = {\n      ...msg,\n      from: this.nodeId,\n      seq: this.keyPair ? ++this.seqCounter : msg.seq,\n    };\n    if (this.keyPair) {\n      return { ...base, signature: signMessage(base, this.keyPair.privateKeyPem) };\n    }\n    return base;\n  }\n\n  /** Deliver an inbound message to a target's handler, with optional sig + replay checks. */\n  private async deliver(target: LocalTransport, msg: ConsensusMessage): Promise<ConsensusReply> {\n    if (target.closed) throw new Error(`LocalTransport: peer ${target.nodeId} is closed`);\n    // Verification path — only when the *target* expects signed messages.\n    if (target.keyPair && target.resolvePeerPublicKey) {\n      const pub = target.resolvePeerPublicKey(msg.from);\n      if (!pub || !verifyMessage(msg, pub)) {\n        throw new Error(`LocalTransport: signature verification failed for message from ${msg.from}`);\n      }\n      // Replay defense: seq must be strictly increasing per sender.\n      if (typeof msg.seq === 'number') {\n        const last = target.lastSeenSeq.get(msg.from) ?? 0;\n        if (msg.seq <= last) throw new Error(`LocalTransport: replayed/out-of-order seq from ${msg.from} (${msg.seq} <= ${last})`);\n        target.lastSeenSeq.set(msg.from, msg.seq);\n      }\n    }\n    if (!target.handler) return null;\n    const reply = await target.handler(msg);\n    return (reply ?? null) as ConsensusReply;\n  }\n\n  async send(to: string, msg: Omit<ConsensusMessage, 'from'>, timeoutMs?: number): Promise<ConsensusReply> {\n    if (this.closed) throw new Error('LocalTransport: closed');\n    const target = this.registry.get(to);\n    if (!target) throw new Error(`LocalTransport: unreachable peer ${to}`);\n    const stamped = this.stamp(msg);","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/swarm/src/consensus/transport.ts#L224-L260","documentation":"LocalTransport verifies inbound messages with Ed25519 when the receiving node was constructed with both keyPair and resolvePeerPublicKey. This error means verification failed: either no public key is known for msg.from, the signature is missing (verifyMessage fails closed on unsigned messages), or the signature does not match the canonicalized message content. It is the transport's fail-closed security gate against forged or tampered consensus messages.","triggerScenarios":"(1) Sender transport was created without a keyPair, so stamp() emitted an unsigned message to a verifying target. (2) target.resolvePeerPublicKey(msg.from) returns undefined because the sender's public key was never registered in the peer key map. (3) Sender signs with a different key than the one registered for its nodeId. (4) Message content was mutated between stamping and delivery (signature covers deep-sorted-key JSON of all fields except signature).","commonSituations":"Enabling signing on only some nodes of a swarm ('partial key material'); rotating node keys without updating every peer's resolvePeerPublicKey map; mixing transports built with generateNodeKeyPair() at different times so each node knows only its own key; tests copying messages and tweaking payload fields before redelivery.","solutions":["Give every consensus node a keyPair via generateNodeKeyPair() so all outbound messages are signed","Build a shared nodeId -> publicKeyPem map and pass the same resolvePeerPublicKey closure to every transport","Verify the sender actually signs: msg.signature must be present (unsigned -> fail-closed false)","After any key rotation, redistribute public keys to all peers before resuming traffic","Never mutate a ConsensusMessage after it is stamped — the signature covers all content fields"],"exampleFix":"// before (only the target verifies; sender unsigned -> always fails)\nconst a = new LocalTransport('a');\nconst b = new LocalTransport('b', { keyPair: kb, resolvePeerPublicKey: () => undefined });\nawait a.send('b', { type: 'request-vote', payload: {} }); // throws\n\n// after (both sides sign; shared pubkey map)\nconst keys = new Map([['a', ka.publicKeyPem], ['b', kb.publicKeyPem]]);\nconst a = new LocalTransport('a', { keyPair: ka, resolvePeerPublicKey: id => keys.get(id) });\nconst b = new LocalTransport('b', { keyPair: kb, resolvePeerPublicKey: id => keys.get(id) });\nawait a.send('b', { type: 'request-vote', payload: {} }); // verifies","handlingStrategy":"validation","validationCode":"// Before first send, assert the key topology is complete on every verifying node:\nimport { verifyMessage } from './transport.js';\nfunction assertKeyWiring(nodes, resolvePeerPublicKey) {\n  for (const n of nodes) {\n    if (!n.hasKeyPair) continue; // verifier-only check\n    for (const peer of nodes) {\n      const pub = resolvePeerPublicKey(peer.nodeId);\n      if (!pub) throw new Error(`missing pubkey for ${peer.nodeId}`);\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try { await transport.send(to, msg); }\ncatch (e) {\n  if (e instanceof Error && e.message.includes('signature verification failed')) {\n    // security failure: quarantine the sender/peer key map, alert; do NOT retry blindly\n    await securityAudit.logBadSignature(to); throw e;\n  }\n  throw e;\n}","preventionTips":["Provision keypairs for ALL nodes at construction time (generateNodeKeyPair), not just some","Distribute one shared nodeId->publicKeyPem map to every transport's resolvePeerPublicKey","Never mutate a stamped ConsensusMessage; rebuild it and re-sign","On key rotation, update every peer's key map before resuming traffic"],"tags":["consensus","ed25519","signature","security","transport","typescript"],"backgroundTag":"signature-verification-failed","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}