Use Walrus Memory from Any Agent Runtime
Agents built on EVM, Base, Virtuals, or any other stack can use Walrus Memory. The runtime your agent executes in and the chain it transacts on are independent of where its memory lives.
Walrus Memory stores memories as encrypted blobs on Walrus and records ownership in a Sui account. Your agent authenticates with an Ed25519 delegate key that the account owner registers on that account. An agent that also signs Ethereum transactions, holds an ERC-20 balance, or runs inside a Virtuals runtime uses Walrus Memory the same way as any other agent.
What does your agent need?
Your agent needs two things, whatever its language or chain:
- An Ed25519 delegate key registered on a Walrus Memory account. Generate an Ed25519 keypair for the agent, then have the account owner add the 32-byte public key to the Sui account object's onchain delegate keys, which grants the agent permission to read and write that account's memory. On each request the relayer verifies the signature and resolves the owner by looking up that public key in the account's onchain delegate keys. See Ownership and Delegates.
- Network access to a relayer. The relayer performs the encryption, storage, and indexing work, so your agent needs no Sui node, Walrus node, or WAL tokens.
Your agent needs no Sui wallet of its own, no bridge, no wrapped token, and no change to how it transacts on its own chain.
Use an SDK
For agents in TypeScript or Python, the SDK signs each request, builds and caches the Seal session key, and polls asynchronous jobs for you:
Source: guides/agent-runtimes.mdimport { MemWal } from "@mysten-incubation/memwal";
const memwal = MemWal.create({
key: process.env.MEMWAL_PRIVATE_KEY,
accountId: process.env.MEMWAL_ACCOUNT_ID,
// Mainnet relayer. Use https://relayer.dev.memwal.ai for Testnet.
serverUrl: "https://relayer.memory.walrus.xyz",
namespace: "agent-memory",
});
await memwal.health();
await memwal.remember("The user prefers dark mode.");
const hits = await memwal.recall({ query: "user preferences", limit: 5 });
To load credentials from the environment, validate connectivity at boot, and handle credential errors, see Headless SDK Setup. For the write-confirm-recall cycle, see the Agent Storage Loop.
How to call Walrus Memory from a language with no SDK
For agents in a language with no SDK, such as Go, Rust, or Elixir, call the relayer API directly. Every authenticated route takes the same signed headers, so your agent needs an Ed25519 signer, SHA-256, and an HTTP client.
Build this canonical message and sign it:
Source: guides/agent-runtimes.md{timestamp}.{method}.{path_and_query}.{body_sha256}.{nonce}.{account_id}
Send the result as hex in these headers:
| Header | Value |
|---|---|
x-public-key | Hex-encoded Ed25519 public key, 32 bytes |
x-signature | Hex-encoded Ed25519 signature of the message above, 64 bytes |
x-timestamp | Unix timestamp in seconds, valid for five minutes |
x-nonce | UUID v4, which the relayer records for replay protection |
x-account-id | The account object ID, the same value as the {account_id} field of the signed message. Official SDKs always send it |
The relayer verifies the signature, then resolves the owner by looking up your public key in the account's onchain delegate keys. For every route, request shape, and response shape, see the Relayer API Reference.
Reads need a Seal credential too
Signed headers authenticate the caller, but they do not decrypt anything. The routes that return stored memories, /api/recall, /api/ask, and /api/restore, also need a Seal credential, and the relayer rejects the call without one:
SEAL credential required (x-seal-session, x-delegate-key, or SERVER_SUI_PRIVATE_KEY)
Supply it one of three ways:
| Credential | How it works |
|---|---|
x-seal-session | A base64 exported Seal SessionKey. The official SDKs build, cache, and send this, and it is the path to follow for new clients |
x-delegate-key | The legacy delegate private key header. Deprecated, and it hands the relayer your key |
| Server fallback | A relayer configured with SERVER_SUI_PRIVATE_KEY decrypts on its own key, so the client sends neither header |
A client that sends only the five signed headers authenticates successfully and then fails to decrypt, which is the failure to expect if recall returns nothing usable. Building a SessionKey outside the SDK means implementing the Seal client flow yourself, so plan for that work before choosing the direct path for reads. Writes through /api/remember do not need it.
Confirm the agent reaches the relayer before you debug signing, using the unauthenticated health route. Set MEMWAL_RELAYER_URL to your relayer's base URL, for example https://relayer.memory.walrus.xyz for Mainnet or https://relayer.dev.memwal.ai for Testnet:
$ curl -sS "$MEMWAL_RELAYER_URL/health"
What cross-chain support does not include
Walrus Memory does not do the following. A design that assumes otherwise does not work:
- Walrus Memory never mirrors, wraps, or relays a memory onto another chain. Ownership records live on Sui.
- An Ethereum address cannot own a memory account. The delegate key is Ed25519, and the account is a Sui object.
- Walrus charges storage in WAL. Your agent neither holds nor spends it when a relayer fronts that cost.
To link an EVM contract to a memory account, model the mapping in your own application: store it in your contract or database, and have your agent present the matching delegate key. For how the embed, store, and recall loop works underneath, see How AI Agent Memory Works.
Timestamp format and clock drift
The {timestamp} field in the canonical message is the same string you send in x-timestamp: Unix time in whole seconds, written as a decimal integer. The relayer accepts a timestamp within ±300s (5 minutes) of its own clock. A millisecond value always falls outside that window, so every request returns 401 and the relayer sets ERR_TIMESTAMP_OUT_OF_BOUNDS on the x-auth-error header. Keep the agent's clock synchronized, and use a fresh timestamp and nonce for each request.
Build the Seal session header
The x-seal-session value is a base64-encoded JSON object holding an exported Seal SessionKey with these fields: address, packageId, mvrName, creationTimeMs, ttlMin, personalMessageSignature, and sessionKey. The relayer decodes the header, parses the JSON, and passes it to SessionKey.import, so your client must produce exactly that shape. The TypeScript SDK signs the session with the delegate key, scopes it to a single packageId that it reads from the relayer's public /config endpoint, and gives it a 5-minute TTL. Use the SDK's buildSealSession method as the reference implementation when you port this flow to another language.