Persistent Agent Memory

Agents are increasingly stateless and serverless — they spin up, do work, and die. Mycelium gives them durable, portable, verifiable memory without putting the data on-chain.

The model: a big, mutable off-chain store (local JSON or Firestore) committed on-chain by a tiny, constant-size anchor — just a SHA-256 root hash, a fetch URI, an ACL, and a monotonic version. Anyone can verify an agent's memory by re-hashing the blob and comparing it to the on-chain root.

The Model

Agent code
off-chain
remember("key", "value")recall("key") → "value"
AgentMemory
agent_memory.py · High-level API
anchor()verify()rehydrate()
Backend
file / firestore

Off-chain key-value store. Holds the actual memory data.

MemoryAnchorClient
anchor.py

On-chain MemoryAnchor contract. Stores the SHA-256 root hash.

The key insight: the anchor contract stores O(1) data per agent regardless of how much memory the agent has. Whether an agent remembers 10 facts or 10 million, the on-chain cost is a single 32-byte hash write.

AgentMemory API

AgentMemory is the high-level interface. It wraps a backend and an optional anchor client:

remember(key: str, value: Any, namespace?: str) → None

Store a key-value pair. Values are JSON-serialized. Optional namespace for isolation.

recall(key: str, namespace?: str) → Any | None

Retrieve a value by key. Returns None if not found.

forget(key: str, namespace?: str) → None

Delete a key-value pair.

anchor() → AnchorResult

Compute the SHA-256 root hash of all memory, upload the blob to the backend's fetch URI, and commit the hash on-chain via set_anchor(). Returns the new version.

verify() → bool

Re-hash local memory, fetch the on-chain anchor, and compare roots. Returns True if they match.

rehydrate() → None

Fetch the blob from the on-chain anchor's URI, verify its hash matches the on-chain root, and replace local memory with the fetched state. This is how an agent restores memory on a new machine.

agent.pypython
from mycelium import AgentContext
from mycelium_sdk.memory import AgentMemory

ctx = AgentContext("wallet.json", "testnet", "pass")
mem = AgentMemory(ctx, backend="file")  # or "firestore"

# Store knowledge
mem.remember("best_model", "gemini-2.0-flash")
mem.remember("task_count", 42)
mem.remember("preferences", {"style": "concise"})

# Retrieve
model = mem.recall("best_model")  # "gemini-2.0-flash"

# Commit on-chain
result = mem.anchor()
print(f"Anchored v{result.version}, root={result.root_hash[:16]}...")

# Later, on a different machine
mem2 = AgentMemory(ctx, backend="file")
mem2.rehydrate()  # restores all key-value pairs
assert mem2.recall("best_model") == "gemini-2.0-flash"

Portability

Because the anchor stores only a hash + URI, an agent's memory is portable across machines, clouds, and runtimes:

  • Spin up anywhere: call rehydrate() on boot to restore memory from the on-chain anchor.
  • Verify integrity: call verify() to confirm local state matches the chain.
  • Survive crashes: the last anchored state is always recoverable.
  • Cross-agent trust: any agent can verify another agent's memory by fetching their anchor and re-hashing.

Backends

Two interchangeable backends, both implementing the same interface:

FileMemoryBackend

JSON file on disk. Default for local development. Zero infrastructure. Memory stored at .mycelium/memory.json.

FirestoreMemoryBackend

Google Cloud Firestore. Production-grade, multi-agent, cloud-native. Memory stored at agent_memory/{agent}/entries/{key}.

Both backends produce identical SHA-256 root hashes for the same data, so you can anchor from one backend and rehydrate into another.

Anchoring Policy

When should an agent anchor? The SDK supports configurable policies:

  • On job completion: anchor after every finalize to checkpoint knowledge gained from the task.
  • Heartbeat: anchor on a timer (e.g. every hour) for long-running agents.
  • Manual: anchor explicitly when the agent decides its memory has changed enough.
  • On shutdown: anchor in a shutdown hook to preserve state before exit.

Each anchor costs one on-chain transaction (~100 stroops on testnet). The data itself stays off-chain, so anchor frequency trades cost for recency.

CLI Commands

The mycelium memory command group exposes the full memory API:

terminalbash
# Store and retrieve
mycelium memory remember "best_model" "gemini-2.0-flash"
mycelium memory recall "best_model"
# → gemini-2.0-flash

# Commit on-chain
mycelium memory anchor
# → ✓ Anchored v3 at CAC27VK..., root=a1b2c3...

# Verify and restore
mycelium memory verify
# → ✓ Local memory matches on-chain anchor v3

mycelium memory rehydrate
# → ✓ Restored 47 entries from anchor v3

mycelium memory status
# → Backend: file, Keys: 47, Anchored: v3, Last anchor: 2024-01-15T10:30:00Z
The MemoryAnchor contract address is set in mycelium.toml under [memory].anchor_address. The default points to the shared testnet deployment at CAC27VKJEPDJJNI36NP7D7VH6WCHT6N5EITKSKPZIQNWA2VPEPBIXJSB.
Mycelium v0.5.0 · Stellar Multi-Network