SAP Explorer Docs
Synapse Agent Protocol

Memory Systems

Two on-chain memory architectures for agent conversation data. Ledger ring buffers and encrypted Vault storage.

Memory Systems

SAP provides two memory systems for persisting agent conversation data on-chain. They solve the same fundamental problem (durable, verifiable session memory) but with different tradeoffs.

Why On-Chain Memory?

Traditional AI agents store conversation history in databases owned by the service provider. If the provider shuts down, changes their terms, or gets hacked, that data is lost or compromised. On-chain memory solves this by storing data directly on Solana, where it is:

  • Owned by the agent, not by a cloud provider
  • Publicly verifiable, so a consumer can prove what was discussed
  • Durable, lasting as long as the Solana blockchain itself
  • Cost-efficient, with writes as cheap as $0.001 per 200 operations

The key challenge is that blockchain storage is expensive. Storing 1 KB on Solana costs approximately 0.007 SOL in rent. For an agent processing thousands of conversations, naively storing everything would be prohibitively expensive. SAP's two memory systems solve this through clever engineering.

Comparison

Ledger (recommended)Vault (legacy)
Architecture4 KB ring buffer PDAEncrypted epoch pages
Write cost~0.000005 SOL (TX fee only)~0.003 to 0.01 SOL (rent per page)
Init cost~0.032 SOL (one-time rent)~0.005 SOL (vault + session)
EncryptionNone (cleartext on-chain)AES-256-GCM (client-side)
StructureRing buffer with sealed pagesEpoch pages with sequence numbers
IntegrityRolling Merkle hashContent hash per inscription
Best forHigh-frequency writes, logs, conversation historySensitive data, encrypted storage
DelegationNoYes (hot wallet delegates)
High-level APISessionManagerManual (module methods)

Use the Ledger via SessionManager for most workloads. Use Vault only when you need client-side encryption or hot-wallet delegation.

Ledger

The Ledger is a fixed-cost ring buffer optimized for high-frequency writes. After the one-time PDA creation (~0.032 SOL), every write costs only the Solana transaction fee (~0.000005 SOL) with zero additional rent. Data is written simultaneously to the ring buffer (for latest-read access) and the transaction log (for permanent storage).

The key insight: Solana charges rent for account storage, but transaction logs are free to store permanently. The Ledger exploits this by keeping only a small rotating buffer in the account (for fast reads) while writing the permanent record into the transaction log (for archival). You get the best of both worlds: fast reads from the ring buffer, permanent storage from the TX logs, and near-zero cost per write.

How It Works

Imagine a whiteboard with space for 10 messages. When you fill it up, you erase the oldest message and write the new one in its place. That is a ring buffer. The whiteboard itself costs money to rent (the PDA), but writing on it is essentially free (just the transaction fee).

Meanwhile, a camera takes a photo of every message before it can be erased (the transaction log). So even though the whiteboard only shows the latest 10 messages, the complete history is preserved in the photos.

MemoryLedger PDA
  ┌──────────────────────────────┐
  │     4 KB Ring Buffer         │
  │  [msg1] [msg2] [msg3] [...]  │
  │  (overwrites oldest when full)│
  └──────────────────────────────┘
  merkleRoot: 0xabc...
  numEntries: 3
  numPages: 0

            │ seal()

  LedgerPage PDA (0)    ~0.031 SOL, write-once
  (snapshot of ring buffer)

Initialize

import { SapClient } from "@oobe-protocol-labs/synapse-sap-sdk";
import { AnchorProvider } from "@coral-xyz/anchor";

const client = SapClient.from(AnchorProvider.env());

// sessionPda: the session PDA this ledger belongs to.
// The ledger PDA is derived as ["sap_ledger", sessionPda].
// Cost: ~0.032 SOL rent deposit (reclaimable when you close the ledger).
await client.ledger.init(sessionPda);

Write

import { sha256, hashToArray } from "@oobe-protocol-labs/synapse-sap-sdk/utils";

// Data to write: accepts Buffer, Uint8Array, or string.
// Maximum size per write: 750 bytes (LIMITS.MAX_LEDGER_WRITE_SIZE).
const data = Buffer.from("User requested SOL to USDC swap");

// contentHash: SHA-256 hash of the data, converted to number[].
// Used for the rolling Merkle tree that ensures data integrity.
const contentHash = hashToArray(sha256(data));

// write() does three things atomically:
//   1. Appends to the ring buffer (overwrites oldest entry when full)
//   2. Emits data in the transaction log (permanent, parseable via getTransaction)
//   3. Updates the rolling Merkle hash on the ledger PDA
await client.ledger.write(sessionPda, data, contentHash);
// Cost: ~0.000005 SOL (TX fee only, zero additional rent)

Seal

// seal() creates a new LedgerPage PDA containing a snapshot of
// the current ring buffer, then resets the buffer for new writes.
// Page PDA is derived as ["sap_page", ledgerPda, pageIndex_u32_le].
await client.ledger.seal(sessionPda);
// Cost: ~0.031 SOL rent (write-once, permanent, NOT reclaimable)

Sealing creates a new LedgerPage PDA containing a snapshot of the current ring buffer, resets the ring buffer for new writes, and increments numPages on the ledger. Pages are immutable after creation.

When to seal: Think of sealing as "saving a chapter." You would not save after every sentence (too expensive). Instead, you write an entire conversation, and seal when the conversation is complete. In practice, most agents seal once per user session, or once per day for high-volume services.

Read and Decode

// Fetch the raw ledger account data
const ledger = await client.ledger.fetchLedger(sessionPda);

// Decode the ring buffer bytes into individual entries.
// Returns an array of Uint8Array entries (length-prefixed u16 LE format).
const entries = client.ledger.decodeRingBuffer(ledger.ring);
entries.forEach((entry) => {
  const text = Buffer.from(entry).toString("utf-8");
  console.log(text); // "User requested SOL to USDC swap"
});

// Ring buffer capacity: 4,096 bytes (LIMITS.RING_CAPACITY).
// Oldest entries are overwritten when full.

Close

await client.ledger.close(sessionPda);
// Reclaims ~0.032 SOL

Vault

The Vault provides encrypted, session-scoped memory with epoch-based pagination. Data is encrypted client-side with AES-256-GCM before being inscribed on-chain. The vault also supports hot-wallet delegation, allowing authorized delegates to inscribe data without the owner's signature.

When you need a Vault instead of a Ledger: If your agent handles sensitive data (personal information, financial details, medical records), the data should not be readable by anyone scanning the blockchain. The Vault encrypts everything client-side before writing, so on-chain observers see only encrypted bytes. Only the holder of the encryption key can read the actual content.

Initialize Vault

import { SapClient } from "@oobe-protocol-labs/synapse-sap-sdk";
import { AnchorProvider } from "@coral-xyz/anchor";

const client = SapClient.from(AnchorProvider.env());

// vaultNonce: 32 random bytes used as encryption salt.
// Generate once and store securely — you need this nonce to
// derive the same vault PDA in future sessions.
const vaultNonce = Array.from(crypto.getRandomValues(new Uint8Array(32)));
await client.vault.initVault(vaultNonce);
// Creates a MemoryVault PDA at ["sap_vault", agentPda]

Open Session

import { sha256, hashToArray } from "@oobe-protocol-labs/synapse-sap-sdk/utils";

// Sessions are scoped by a unique ID. The ID is SHA-256 hashed
// to derive the session PDA: ["sap_session", vaultPda, sessionHash].
const sessionId = "conversation-456";
const sessionHash = hashToArray(sha256(sessionId)); // number[] (32 bytes)
await client.vault.openSession(sessionHash);

Inscribe Data

await client.vault.compactInscribe(sessionPda, vaultPda, {
  sequence: 1,                               // ordering number within the session
  encryptedData: Buffer.from(encryptedBytes), // AES-256-GCM encrypted payload (max 750 bytes)
  nonce: encryptionNonce,                     // 12-byte encryption nonce (number[])
  contentHash: contentHashArray,              // SHA-256 hash of the plaintext (number[], 32 bytes)
});

Delegation (Hot Wallets)

Vaults support delegated access for scenarios where a hot wallet needs to write on behalf of the vault owner. This is essential for production services where you do not want your main keypair sitting on a server.

Real-world analogy: A delegate is like giving a trusted employee access to a specific safe in your office. They can deposit items (write data) but only during business hours (expiry time), and you can revoke their access at any moment.

await client.vault.addDelegate(
  hotWallet,                                   // PublicKey of the delegate wallet
  0xFF,                                        // permission bitmask: 0xFF = full write access
  Math.floor(Date.now() / 1000) + 86400,       // Unix timestamp: expires in 24 hours
);

// Revoke access immediately when no longer needed
await client.vault.revokeDelegate(hotWallet);

Cost Summary

OperationLedger CostNotes
init / start~0.032 SOLOne-time rent for 4 KB ring buffer (reclaimable)
write~0.000005 SOLTX fee only, zero rent
seal~0.031 SOLPermanent page, write-once
closeReclaims ~0.032 SOLReturns ring buffer rent
100 writes~0.0005 SOLJust transaction fees
1,000 writes~0.005 SOLStill just transaction fees

For high-frequency writes, the ledger is orders of magnitude cheaper than the vault.

Choosing Between Ledger and Vault

Use Ledger when you need high-frequency writes, cost per write matters, data does not need to be encrypted on-chain, or you want the simplest API via SessionManager. This covers the vast majority of use cases: conversation logs, activity tracking, context storage for AI interactions.

Use Vault when data must be encrypted on-chain (AES-256-GCM), you need hot-wallet delegation for backend services, you need epoch-based pagination with custom sequencing, or you are working with an existing Vault-based codebase.

If you are unsure, use the Ledger. It is simpler, cheaper, and sufficient for most agent workloads. You can always add a Vault later for sensitive data without affecting your existing Ledger setup.