Docs/Integrations/A2A Communication

A2A Communication

Instrument agent-to-agent communication with bilateral cryptographic signatures for tamper-evident proof of message exchange.

import { A2AChannel, createInstrumentedFetch } from '@invariance/sdk';
Prerequisites: Initialization, Sessions & Receipts, Identity System

Overview

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.

Architecture

A2AChannel operates on top of a Session. The sender signature covers hash(payload + sender + receiver + timestamp). The receiver only counter-signs verified messages.

Important Notes

Both agents need private keys
Both sender and receiver need Ed25519 private keys for bilateral signing.

Quick Example

Bilateral agent communicationtypescript
// 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 established

Type Definitions

A2AEnvelope
interface A2AEnvelope {
  payload: unknown;
  sender: string;
  receiver: string;
  timestamp: string;
  sender_signature: string;
  payload_hash: string;
  trace_node_id: string;
  session_id: string;
}
Envelope wrapping an agent-to-agent message with sender signature.

API Reference

constructor
Create an A2A channel for an agent.
new A2AChannel(session: Session, agentIdentity: string, privateKey: string, opts?: { apiUrl?: string; apiKey?: string })
Parameters
sessionSessionSession for recording A2A receipts
agentIdentitystringAgent's identity string
privateKeystringEd25519 private key
ReturnsA2AChannel
wrapOutgoing
Wrap an outgoing message with sender signature. Creates an a2a_send receipt.
async wrapOutgoing(to: string, payload: unknown, metadata?: Record<string, unknown>): Promise<{ envelope: A2AEnvelope; receipt: Receipt }>
Parameters
tostringReceiver agent identity
payloadunknownMessage payload
ReturnsPromise<{ envelope: A2AEnvelope; receipt: Receipt }>
wrapIncoming
Receive, verify sender signature, and counter-sign. Creates an a2a_receive receipt.
async wrapIncoming(envelope: A2AEnvelope, senderPublicKey?: string): Promise<{ payload: unknown; verified: boolean; receipt: Receipt }>
Parameters
envelopeA2AEnvelopeReceived envelope
senderPublicKeystringSender's public key (or auto-lookup)
ReturnsPromise<{ payload, verified, receipt }>

Walkthrough

Setting up bilateral agent communication
1
Initialize both agents
Set up the SDK and create sessions for both the sender and receiver agents.
typescript
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' });
2
Create A2A channels
Create a channel for each agent with their private keys.
typescript
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_...',
});
3
Send a message
Agent A sends a message to Agent B. The payload is signed by Agent A.
typescript
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.)
4
Receive and counter-sign
Agent B receives the envelope, verifies Agent A's signature, and counter-signs.
typescript
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 established

Error Handling

Error handling patterntypescript
import { 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'
  }
}

Use Cases

  • Create provable communication trails between AI agents
  • Verify inter-agent messages weren't tampered with in transit
  • Build audit trails for multi-agent coordination and delegation
  • Instrument any protocol (HTTP, WebSocket, MCP) with bilateral signing
On this page
OverviewArchitectureImportant NotesQuick ExampleType DefinitionsAPI ReferenceWalkthroughError HandlingUse CasesRelated Modules