Instrument agent-to-agent communication with bilateral cryptographic signatures for tamper-evident proof of message exchange.
A2A creates bilateral proof of message exchange. The sender signs the payload hash with metadata, the receiver verifies and counter-signs. Both operations create receipts, building a hash-chained audit trail of all agent communication.
A2AChannel is protocol-agnostic — wraps HTTP, WebSocket, message queues, MCP, CrewAI, LangChain. `createInstrumentedFetch()` provides a drop-in fetch() replacement for HTTP-based communication.
A2AChannel operates on top of a Session. The sender signature covers hash(payload + sender + receiver + timestamp). The receiver only counter-signs verified messages.
// Agent A: Create channel and send
const sessionA = inv.session({ agent: 'agent-a', name: 'conversation' });
const channelA = new A2AChannel(sessionA, 'agent-a', privateKeyA, {
apiUrl: 'https://api.invariance.dev', apiKey: 'dev_...',
});
const { envelope } = await channelA.wrapOutgoing('agent-b', {
type: 'request',
data: { query: 'Analyze ETH price trends' },
});
// Agent B: Receive and counter-sign
const sessionB = inv.session({ agent: 'agent-b', name: 'conversation' });
const channelB = new A2AChannel(sessionB, 'agent-b', privateKeyB);
const { payload, verified } = await channelB.wrapIncoming(envelope, publicKeyA);
console.log(verified); // true — bilateral proof establishedinterface A2AEnvelope {
payload: unknown;
sender: string;
receiver: string;
timestamp: string;
sender_signature: string;
payload_hash: string;
trace_node_id: string;
session_id: string;
}import { Invariance, A2AChannel } from '@invariance/sdk';
const inv = Invariance.init({ apiKey: 'dev_...' });
const sessionA = inv.session({ agent: 'agent-a', name: 'conversation-1' });
const sessionB = inv.session({ agent: 'agent-b', name: 'conversation-1' });const channelA = new A2AChannel(sessionA, 'agent-a', privateKeyA, {
apiUrl: 'https://api.invariance.dev',
apiKey: 'dev_...',
});
const channelB = new A2AChannel(sessionB, 'agent-b', privateKeyB, {
apiUrl: 'https://api.invariance.dev',
apiKey: 'dev_...',
});const { envelope, receipt: sendReceipt } = await channelA.wrapOutgoing(
'agent-b',
{ type: 'request', data: { query: 'Analyze ETH price trends' } },
);
console.log(envelope.sender_signature); // Ed25519 signature
console.log(sendReceipt.action); // 'a2a_send'
// Transport the envelope to Agent B (HTTP, WebSocket, etc.)const { payload, verified, receipt: recvReceipt } = await channelB.wrapIncoming(
envelope,
publicKeyA, // verify sender's signature
);
console.log(verified); // true
console.log(payload.data.query); // 'Analyze ETH price trends'
console.log(recvReceipt.action); // 'a2a_receive'
// Bilateral proof is now establishedimport { InvarianceError } from '@invariance/sdk';
try {
// Receiving with wrong sender public key
const { verified } = await channelB.wrapIncoming(envelope, wrongPublicKey);
if (!verified) {
console.warn('Sender signature verification failed');
// Message may have been tampered with or sent by a different agent
}
} catch (err) {
if (err instanceof InvarianceError) {
console.error(err.code); // 'SIGNATURE_INVALID'
console.error(err.message); // 'Sender signature does not match'
}
}