Documentation

Shared, verifiable memory for multi-agent systems. Pools, ACLs, audit logs, and a built-in treasury — all on-chain.

Quick Start

1. Install
TypeScript
npm install @forestinfra/shelmem
Python
pip install shelmem
2. Initialise
import { ShelMem, openaiEmbeddings } from '@forestinfra/shelmem';

const mem = new ShelMem({
  supabaseUrl: process.env.SUPABASE_URL,
  supabaseKey: process.env.SUPABASE_KEY,
  encrypt: true,
  embeddingProvider: openaiEmbeddings(OPENAI_KEY),
});
3. Write a memory
const result = await mem.write(
  'agent-001', 'User prefers dark mode', 'preferences', 'preference'
);
// → { content_hash, shelby_object_id, aptos_tx_hash }
4. Recall + verify
const memories = await mem.recall('agent-001', 'preferences');
for (const m of memories) {
  console.log(m.memory, m.verified); // true | false | null
}
5. Semantic search
const results = await mem.search('what does the user prefer?');
// → [{ memory_preview, similarity: 0.91 }]

API Reference

write(agent_id, memory, context, memory_type?, metadata?, treasury?)Store a memory on Shelby. Content is SHA-256 hashed, optionally encrypted, and embedding stored if provider configured. Treasury fields (amount, currency, counterparty, tx_status) can be passed for financial records.Parameters
agent_idstringAgent identifier
memorystringContent to store
contextstringCategory label
memory_typeMemoryType?'fact'|'decision'|'preference'|'observation'|'transaction_record'|'balance_snapshot'|'spending_policy'
metadataobject?Key-value metadata
treasuryTreasuryFields?{ amount?, currency?, counterparty?, tx_status? }
Returns
shelby_object_idstringShelby address
aptos_tx_hashstringOn-chain tx hash
content_hashstringSHA-256 of plaintext
memory_typestringType stored
timestampstringISO 8601
amountnumber?Transaction amount
currencystring?Currency (APT, USDT, etc)
recall(agent_id, context?, limit?, memory_type?)Retrieve memories. Each is decrypted and verified against its stored content hash.Parameters
agent_idstringAgent to query
contextstring?Filter by context
limitnumber?Max results (default 10)
memory_typeMemoryType?Filter by type
Returns
memorystringContent (decrypted)
verifiedboolean|nulltrue=authentic, false=tampered
memory_typestringMemory type
content_hashstringSHA-256 hash
timestampstringISO 8601
search(query, agent_id?, limit?, threshold?)Semantic search by meaning using vector similarity. Requires embeddingProvider.Parameters
querystringNatural language query
agent_idstring?Filter to agent
limitnumber?Max results (default 10)
thresholdnumber?Min similarity 0-1
Returns
memory_previewstringFirst 200 chars
similaritynumberCosine similarity
memory_typestringType
agent_idstringAgent
verify(id)Re-download from Shelby, decrypt, and verify content hash.Parameters
idstringMemory UUID
Returns
verifiedbooleanHash matches
content_hashstringActual hash
expected_hashstringStored hash
delete(id)Remove a memory from Supabase and attempt Shelby blob deletion.Parameters
idstringMemory UUID
recordTransaction(params)Convenience wrapper for write(). Sets memory_type='transaction_record', defaults tx_status to 'pending'. Treasury records get 365-day Shelby expiry.Parameters
agentIdstringAgent identifier
memorystringDescription of the transaction
contextstringCategory label
amountnumberTransaction amount
currencystringCurrency (APT, USDT, etc)
counterpartystringWallet address of payer/payee
txStatusstring?'pending' (default) | 'confirmed' | 'failed'
Returns
shelby_object_idstringShelby address
content_hashstringSHA-256 hash
amountnumberAmount
currencystringCurrency
tx_statusstringStatus
recordBalanceSnapshot(params)Convenience wrapper for write(). Sets memory_type='balance_snapshot'. Records a point-in-time balance.Parameters
agentIdstringAgent identifier
memorystringBalance description
contextstringCategory label
amountnumberBalance amount
currencystringCurrency
Returns
shelby_object_idstringShelby address
amountnumberBalance amount
currencystringCurrency
getLatestBalance(agentId)Returns the most recent balance_snapshot for an agent, or null if none exist.Parameters
agentIdstringAgent identifier
Returns
memorystringBalance description
amountnumberBalance amount
currencystringCurrency
timestampstringWhen recorded
createPool({ name, ownerAgentId, description?, metadata? })Create a shared memory pool. The owner is automatically added as a member with role 'owner'.Parameters
namestringPool name
ownerAgentIdstringAgent that will own the pool
descriptionstring?Optional description
metadataobject?Optional metadata
Returns
idstringPool UUID
namestringPool name
owner_agent_idstringOwner
created_atstringISO 8601
addPoolMember(poolId, callerAgentId, targetAgentId, role)Add or update a member's role. Caller must be the pool owner.Parameters
poolIdstringPool UUID
callerAgentIdstringMust have role owner
targetAgentIdstringAgent to add
role'owner'|'writer'|'reader'Role to grant
Returns
pool_idstringPool UUID
agent_idstringMember agent
rolestringRole granted
added_atstringISO 8601
removePoolMember(poolId, callerAgentId, targetAgentId)Remove a member. Caller must be the pool owner. The owner cannot be removed.Parameters
poolIdstringPool UUID
callerAgentIdstringMust have role owner
targetAgentIdstringAgent to remove
listPoolMembers(poolId, callerAgentId)List all members of a pool. Caller must be a pool member.Parameters
poolIdstringPool UUID
callerAgentIdstringMust be a member
Returns
agent_idstringMember agent
rolestringRole
added_atstringWhen added
listPools(agentId)List all pools the agent is a member of.Parameters
agentIdstringAgent identifier
Returns
idstringPool UUID
namestringPool name
owner_agent_idstringOwner
writeToPool({ poolId, agentId, memory, context, memory_type?, metadata?, treasury? })Write a memory into a shared pool. Caller must have role 'owner' or 'writer'. Throws PermissionError otherwise.Parameters
poolIdstringPool UUID
agentIdstringCaller — must be owner or writer
memorystringContent to store
contextstringCategory label
memory_typeMemoryType?Default: observation
metadataobject?Optional metadata
treasuryTreasuryFields?Optional { amount, currency, counterparty, tx_status }
Returns
shelby_object_idstringShelby address
aptos_tx_hashstringOn-chain tx hash
content_hashstringSHA-256 of plaintext
memory_typestringType stored
timestampstringISO 8601
recallFromPool({ poolId, agentId, context?, limit?, memory_type? })Read memories written into a shared pool by any member. Caller must be a pool member (any role). Each memory is verified against its content hash. Records a 'read' entry in the pool audit log.Parameters
poolIdstringPool UUID
agentIdstringCaller — must be a member
contextstring?Filter by context
limitnumber?Max results (default 10)
memory_typeMemoryType?Filter by type
Returns
memorystringContent (decrypted, verified)
agent_idstringWhich agent wrote it
pool_idstringPool UUID
memory_typestringType
verifiedboolean|nullHash verification result
recallShared(agent_id, context?, limit?, memory_type?)Read memories that were explicitly shared with this agent via the per-memory shared_with ACL — independent of pool membership.Parameters
agent_idstringAgent identifier
contextstring?Filter by context
limitnumber?Max results (default 10)
memory_typeMemoryType?Filter by type
Returns
memorystringContent (decrypted, verified)
agent_idstringOriginal writer
shared_withstring[]ACL list
searchPool({ poolId, agentId, query, limit?, threshold? })Semantic search inside a shared pool using pgvector. Caller must be a pool member. Requires an embeddingProvider in config.Parameters
poolIdstringPool UUID
agentIdstringCaller — must be a member
querystringNatural language query
limitnumber?Max results (default 10)
thresholdnumber?Min similarity 0-1 (default 0.5)
Returns
idstringMemory UUID
agent_idstringWriter
memory_previewstringFirst 200 chars
similaritynumberCosine similarity
getPoolAuditLog(poolId, callerAgentId, limit?)Read the access log for a pool. Owner only. One row per writeToPool / recallFromPool / searchPool call.Parameters
poolIdstringPool UUID
callerAgentIdstringMust have role owner
limitnumber?Max entries (default 100)
Returns
action'write'|'read'Action recorded
agent_idstringAgent that acted
memory_idstring|nullSet on writes
result_countnumber|nullSet on reads
created_atstringISO 8601

Agent Treasury

ShelMem includes treasury memory types for AI agent payments. Agents can record transactions, balance snapshots, and spending policies as tamper-proof, verifiable memories. Treasury records get 365-day Shelby expiry (vs 30 days for standard memories).Record a transaction
await mem.recordTransaction({
  agentId: 'trading-agent',
  memory: 'Paid 100 APT for API access',
  context: 'payments',
  amount: 100,
  currency: 'APT',
  counterparty: '0xmerchant...',
});
// → { memory_type: 'transaction_record', tx_status: 'pending', ... }
Record a balance snapshot
await mem.recordBalanceSnapshot({
  agentId: 'trading-agent',
  memory: 'End-of-day balance',
  context: 'treasury',
  amount: 4725,
  currency: 'APT',
});
Get latest balance
const balance = await mem.getLatestBalance('trading-agent');
// → { amount: 4725, currency: 'APT', memory_type: 'balance_snapshot' }
// → null if no balance snapshots exist
Memory types: transaction_record · balance_snapshot · spending_policy
tx_status values: pending · confirmed · failed
Shelby expiry: 365 days (vs 30 days for standard memories)

Shared Pools

Pools are shared memory workspaces. Add agents as members with owner, writer, or reader roles, then any member can read everything written into the pool while role permissions are enforced at the SDK boundary. Use them to coordinate workflows like trading → execution → risk → reporting through a single source of truth.1. Create a pool and invite collaborators
const pool = await mem.createPool({
  name: 'market-ops',
  ownerAgentId: 'trading-agent',
});

await mem.addPoolMember(pool.id, 'trading-agent', 'execution-agent', 'writer');
await mem.addPoolMember(pool.id, 'trading-agent', 'risk-agent',      'writer');
await mem.addPoolMember(pool.id, 'trading-agent', 'reporting-agent', 'reader');
2. Write into the pool (owner or writer)
await mem.writeToPool({
  poolId: pool.id, agentId: 'trading-agent',
  memory: 'RSI=35, buy 500 APT', context: 'trading',
  memory_type: 'decision',
});
3. Read from the pool (any member)
const records = await mem.recallFromPool({
  poolId: pool.id, agentId: 'reporting-agent',
});
// → each record carries agent_id (writer) and pool_id, plus the verified content
4. Permission errors
import { PermissionError } from '@forestinfra/shelmem';

try {
  await mem.writeToPool({
    poolId: pool.id, agentId: 'reporting-agent',
    memory: 'denied', context: 'trading',
  });
} catch (err) {
  if (err instanceof PermissionError) {
    // role 'reader' cannot write
  }
}
Roles: owner (manage members + read/write) · writer (read/write) · reader (read only)
Membership: creating a pool auto-adds the owner. The owner cannot be removed; transfer ownership before deletion.
Migration: apply supabase/migration-v5.sql to add memory_pools, pool_members, and the nullable pool_id column on memories. Existing private memories are unaffected.

Per-Memory ACLs

Sometimes a memory needs to be shared with specific agents but doesn't belong in a pool. Pass sharedWith on write() to grant individual agents read access via recallShared(). Pools and ACLs compose: a memory can live in a pool and be additionally shared with agents outside it.
// Writer side
await mem.write(
  'trading-agent', 'sensitive insight', 'analysis', 'observation',
  /* metadata */ undefined, /* treasury */ undefined,
  /* sharedWith */ ['execution-agent', 'risk-agent'],
);

// Reader side
const visible = await mem.recallShared('execution-agent');
Storage: memories.shared_with TEXT[] with a GIN index. Empty list = private to the writer.

Pool Audit Log

Every writeToPool, recallFromPool, and searchPool call writes a row to pool_access_log. Reads record the result count; writes record the new memory's id. Pool owners can replay the log to see who read what and when.
const log = await mem.getPoolAuditLog(pool.id, 'trading-agent');
// → [{ action: 'read',  agent_id: 'reporting-agent', result_count: 3,  created_at },
//    { action: 'write', agent_id: 'risk-agent',      memory_id: '...', created_at }]
Access: owner only — non-owners get PermissionError.
Best-effort: a failed audit insert never blocks the underlying read/write.

Cryptographic Agent Identity

By default agent_id is a trusted string. For production hardening — proving an agent owns its identity — sign claims with the agent's Aptos Ed25519 private key and verify them at the trust boundary. The primitives are exported; wiring them into RLS or every method call is opt-in (this stays compatible with the trust model already in use).
import { signAgentClaim, verifyAgentClaim } from '@forestinfra/shelmem';

// Agent side: produce a short-lived signed claim
const claim = signAgentClaim('trading-agent', process.env.AGENT_PRIVATE_KEY);
// → { agentId, timestamp, publicKey, signature }

// Verifier side: registry maps agent_id → expected pubkey
const expectedPub = REGISTRY['trading-agent'];
verifyAgentClaim(claim, 'trading-agent', expectedPub); // throws AgentClaimError on failure
Algo: Ed25519 signature over shelmem:agent-claim:v1\n{agent_id}\n{timestamp}.
Replay protection: claims expire after maxAgeSeconds (default 300s).
Same key: the agent's Aptos key already used for on-chain anchoring works directly — no new keys to issue.

Encryption

AES-256-GCM encryption. Key derived from your Aptos private key via HMAC-SHA256. Content hashes are computed on plaintext before encryption so tamper verification still works.
const mem = new ShelMem({
  supabaseUrl, supabaseKey,
  aptosPrivateKey: '0x...',
  encrypt: true,
});
// Write encrypted, recall decrypted, hash verified on plaintext
Format: [IV 12B] [AuthTag 16B] [Ciphertext]
Key: HMAC-SHA256(privkey, "ShelMem-v1")
Algo: AES-256-GCM, random 96-bit IV per write

Configuration

OptionRequiredDescription
supabaseUrlYesSupabase project URL
supabaseKeyYesSupabase API key
aptosPrivateKeyWhen encrypt=trueEd25519 private key
encryptNoEnable AES-256-GCM (default false)
embeddingProviderNoFunction for semantic search
networkNotestnet (default) or shelbynet
mockNotrue (default) for local dev

Integrations

LangChain
Python
from shelmem.integrations.langchain import ShelMemChatMessageHistory

history = ShelMemChatMessageHistory(
    session_id="user-123", agent_id="my-chatbot",
    supabase_url=url, supabase_key=key,
)

from langchain_core.runnables.history import RunnableWithMessageHistory
chain_with_history = RunnableWithMessageHistory(chain, lambda sid: history)
CrewAI
Python
from shelmem.integrations.crewai import ShelMemStorage
crew = Crew(
    agents=[...], tasks=[...], memory=True,
    short_term_memory=ShortTermMemory(storage=ShelMemStorage(
        crew_id="my-crew", supabase_url=url, supabase_key=key,
    )),
)
Vercel AI SDK
TypeScript
import { createShelMemTools } from '@forestinfra/shelmem';
const tools = createShelMemTools({
  agentId: 'my-agent',
  supabaseUrl: process.env.SUPABASE_URL,
  supabaseKey: process.env.SUPABASE_KEY,
});
// gives agents memorize + remember tools

Database Setup

Create a Supabase project, then run this SQL:
SQL
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE IF NOT EXISTS memories (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  agent_id TEXT NOT NULL, context TEXT NOT NULL,
  memory_preview TEXT, shelby_object_id TEXT NOT NULL,
  aptos_tx_hash TEXT, content_hash CHAR(64),
  memory_type TEXT DEFAULT 'observation',
  verified BOOLEAN, embedding vector(1536),
  amount NUMERIC, currency TEXT,
  counterparty TEXT, tx_status TEXT,
  metadata JSONB DEFAULT '{}',
  created_at TIMESTAMPTZ DEFAULT now(),
  updated_at TIMESTAMPTZ DEFAULT now()
);

CREATE INDEX idx_memories_agent_id ON memories(agent_id);
CREATE INDEX idx_memories_agent_context ON memories(agent_id, context);
CREATE INDEX idx_memories_created_at ON memories(created_at DESC);
CREATE INDEX idx_memories_type ON memories(agent_id, memory_type);
CREATE INDEX idx_memories_embedding ON memories USING hnsw (embedding vector_cosine_ops);
ALTER TABLE memories ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Allow all" ON memories FOR ALL USING (true) WITH CHECK (true);

-- Shared pools (migration-v5)
CREATE TABLE memory_pools (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  name TEXT NOT NULL, description TEXT,
  owner_agent_id TEXT NOT NULL,
  metadata JSONB DEFAULT '{}',
  created_at TIMESTAMPTZ DEFAULT now(),
  updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE pool_members (
  pool_id UUID REFERENCES memory_pools(id) ON DELETE CASCADE,
  agent_id TEXT NOT NULL,
  role TEXT CHECK (role IN ('owner','writer','reader')),
  added_at TIMESTAMPTZ DEFAULT now(),
  PRIMARY KEY (pool_id, agent_id)
);
ALTER TABLE memories ADD COLUMN pool_id UUID
  REFERENCES memory_pools(id) ON DELETE SET NULL;

-- ACLs + audit log + pool semantic search (migration-v6)
ALTER TABLE memories ADD COLUMN shared_with TEXT[] DEFAULT '{}';
CREATE INDEX idx_memories_shared_with ON memories USING gin (shared_with);

CREATE TABLE pool_access_log (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  pool_id UUID REFERENCES memory_pools(id) ON DELETE CASCADE,
  agent_id TEXT NOT NULL,
  action TEXT CHECK (action IN ('write','read')),
  memory_id UUID REFERENCES memories(id) ON DELETE SET NULL,
  result_count INT, metadata JSONB DEFAULT '{}',
  created_at TIMESTAMPTZ DEFAULT now()
);
-- Plus the match_pool_memories pgvector RPC — see migration-v6.sql.

Architecture

Flow
Agent → ShelMem SDK → [SHA-256 hash] → [AES-256 encrypt]
                    → Shelby Protocol (encrypted content)
                    → Supabase (metadata + embeddings)
                    → Aptos (on-chain proof)

Recall ← [verify hash] ← [decrypt] ← Shelby download
       ← Supabase metadata query

Shelby

Decentralised hot storage. Encrypted content lives here.

Supabase

Metadata + pgvector embeddings. Agent IDs, hashes, timestamps.

Aptos

On-chain anchoring. Every write submits a transaction as proof.
© 2026 ShelMem — a Forest product.