The cryptographic primitives and data structures that make every agent action provable.
Every agent action produces a receipt containing a SHA-256 hash of the action data plus the hash of the previous receipt (previousHash). The first receipt in a session uses "0" as its previous hash. This creates a tamper-evident chain — modifying any receipt breaks all subsequent hashes.
Receipt 1: hash = SHA256(data₁ + "0")
Receipt 2: hash = SHA256(data₂ + hash₁)
Receipt 3: hash = SHA256(data₃ + hash₂)Because each hash depends on the previous one, inserting, removing, or altering any receipt in the chain produces a mismatch that is immediately detectable during verification.
Every receipt hash is signed with the agent's Ed25519 private key using @noble/ed25519. This proves the receipt was created by the claimed agent — no one else possesses the private key needed to produce a valid signature.
Keys are generated via Invariance.generateKeypair(), which returns a 32-byte private key and its corresponding 32-byte public key, both hex-encoded.
import { Invariance } from '@invariance/sdk';
const { privateKey, publicKey } = Invariance.generateKeypair();
// privateKey: 64-char hex string (32 bytes)
// publicKey: 64-char hex string (32 bytes)Each receipt captures a single agent action with full cryptographic context:
interface Receipt {
id: string; // ULID identifier
sessionId: string; // Parent session
agent: string; // Agent that performed the action
action: string; // Action name
input: Record<string, unknown>;
output?: Record<string, unknown>;
timestamp: number; // Unix ms
hash: string; // SHA-256 of canonical data
previousHash: string; // Previous receipt hash ("0" for first)
signature: string | null; // Ed25519 signature
}"tool_call", "a2a_send").previousHash."0" for the first receipt in a session.Sessions group receipts into ordered chains. Each session tracks the full lifecycle of an agent interaction.
// Lazy creation — session is created on first receipt
const session = inv.session();
// Awaited creation — session exists immediately
const session = await inv.createSession();
// Record actions within the session
await session.record('tool_call', {
input: { tool: 'search', query: 'latest news' },
output: { results: ['...'] },
});
// Close the session when done
await session.close();Sessions have three possible statuses:
Closing a session computes a close hash over the entire receipt chain, sealing the session against further modification.
Verification recomputes every hash from the raw action data and checks each Ed25519 signature. If any receipt has been modified — even a single character — the chain breaks and verification fails.
import { verifyChain } from '@invariance/sdk';
const result = await verifyChain(receipts, publicKey);
if (result.valid) {
console.log('Chain is intact — all hashes and signatures verified');
} else {
console.error('Tamper detected at receipt:', result.brokenAt);
console.error('Reason:', result.reason);
}The verifyChain() function walks the receipt array in order, recomputes each hash from the canonical action data plus the previous hash, and verifies the Ed25519 signature against the agent's public key. A single failure anywhere in the chain marks the entire session as tampered.
When two agents communicate, Invariance creates bilateral proof — both the sender and receiver sign the same message, proving the communication happened and neither party can deny it.
The process works as follows:
a2a_send receipt in its own session.a2a_receive receipt with both signatures.// Agent A: send a signed message
const envelope = await agentA.a2a.send({
to: 'acme/agent-b',
payload: { task: 'review-contract', contractId: 'c-123' },
});
// envelope contains: payload hash, sender signature, metadata
// Agent B: receive and counter-sign
const received = await agentB.a2a.receive(envelope);
// received contains: both sender and receiver signatures
// Both agents now have cryptographic proof of the exchangeThis dual-signature model ensures that neither party can deny sending or receiving a message. The a2a_send and a2a_receive receipts are recorded in each agent's respective session chains, creating an independently verifiable record on both sides.