llms-full.txt@docs · git:20260731.cef9728 · 2026-07-31 · sha256 6a6841e0187a8f6e

llms-full.txt@docs git:20260731.cef9728A

Immutable. This exact content is served forever at /api/v1/blob/6a6841e0187a8f6e.

# MemWal

> MemWal is a privacy-first AI memory layer. It stores encrypted memories on Walrus (decentralized storage) and retrieves them with semantic search. Ownership is enforced onchain via Sui smart contracts. The TypeScript SDK (`@mysten-incubation/memwal`) gives any app persistent, encrypted memory in a few lines of code.

Important notes:

- MemWal is currently in beta
- The SDK talks to a relayer service that handles embedding, SEAL encryption, Walrus upload/download, and vector search
- All content is end-to-end encrypted — only the owner and authorized delegates can decrypt
- Delegate keys provide scoped access — agents can read/write memory without holding the owner's private key
- Memory is scoped by `owner + namespace` — each namespace is an isolated memory space

---

## Quick Start

### Installation

```bash
pnpm add @mysten-incubation/memwal
```

Optional peer dependencies:

```bash
# For Vercel AI SDK middleware
pnpm add ai zod

# For manual client (client-side SEAL encryption)
pnpm add @mysten/sui @mysten/seal @mysten/walrus
```

### Prerequisites

- Node.js v18+ or Bun v1+
- A delegate key and account ID (generate at https://memory.walrus.xyz)
- A relayer URL (use `https://relayer.memory.walrus.xyz` for production or `https://relayer-staging.memory.walrus.xyz` for staging)

### First Memory

```ts
import { MemWal } from "@mysten-incubation/memwal";

const memwal = MemWal.create({
  key: "<your-ed25519-private-key>",
  accountId: "<your-memwal-account-id>",
  serverUrl: "https://relayer.memory.walrus.xyz",
  namespace: "demo",
});

await memwal.health();
const job = await memwal.remember("I live in Hanoi and prefer dark mode.");
await memwal.waitForRememberJob(job.job_id);

const result = await memwal.recall({ query: "What do we know about this user?" });
console.log(result.results);
```

---

## SDK Entry Points

| Entry Point | Import | When to Use |
|---|---|---|
| `MemWal` | `@mysten-incubation/memwal` | **Recommended default** — relayer handles embeddings, SEAL, and storage |
| `MemWalManual` | `@mysten-incubation/memwal/manual` | Client-managed embeddings and local SEAL operations |
| `withMemWal` | `@mysten-incubation/memwal/ai` | Vercel AI SDK middleware — auto recall + save |
| Account utils | `@mysten-incubation/memwal/account` | Account creation, delegate key management |

---

## SDK API Reference

### `MemWal.create(config)`

```ts
MemWal.create(config: MemWalConfig): MemWal
```

Config:

| Property | Type | Required | Default | Notes |
|---|---|---|---|---|
| `key` | `string` | Yes | — | Ed25519 delegate private key in hex |
| `accountId` | `string` | Yes | — | MemWalAccount object ID on Sui |
| `serverUrl` | `string` | No | `https://relayer.memory.walrus.xyz` | Relayer URL |
| `namespace` | `string` | No | `"default"` | Default namespace for memory isolation |

### `remember(text, namespace?): Promise<RememberAcceptedResult>`

Submit one memory through the relayer. The relayer returns after creating a background job; embedding, SEAL encryption, Walrus upload, and vector indexing continue asynchronously.

Returns:
```ts
{
  job_id: string; // Polling id
  status: string; // Usually "running"
}
```

Use `rememberAndWait(text, namespace?, opts?)` or `waitForRememberJob(job_id, opts?)` to resolve the completed `{ id, job_id, blob_id, owner, namespace }` result.

### `recall(params): Promise<RecallResult>`

Search for memories matching a natural language query, scoped to `owner + namespace`.

- Preferred form: `recall({ query, limit?, topK?, namespace?, maxDistance? })`
- `limit` defaults to `10`; `topK` is an alias and wins when both are set
- Legacy positional forms still work: `recall(query)`, `recall(query, limit)`, `recall(query, limit, namespace)`, and `recall(query, options)`
- `maxDistance` filters weak matches client-side by dropping results where `distance >= maxDistance`

Returns:
```ts
{
  results: Array<{
    blob_id: string;   // Walrus blob ID
    text: string;      // Decrypted plaintext
    distance: number;  // Cosine distance (lower = more similar)
  }>;
  total: number;
}
```

### `analyze(text, namespace?): Promise<AnalyzeResult>`

Extract memorable facts from text using an LLM, then return accepted background jobs for storing each fact.

Returns:
```ts
{
  job_ids: string[];
  facts: Array<{
    text: string;     // Extracted fact
    id: string;       // Same value as job_id
    job_id: string;   // Polling id
  }>;
  fact_count: number;
  status: string;     // Usually "pending"
  owner: string;
}
```

Use `analyzeAndWait(text, namespace?, opts?)` to wait for every extracted fact job to finish.

### `restore(namespace, limit?): Promise<RestoreResult>`

Rebuild missing indexed entries for one namespace from Walrus. Incremental — only re-indexes blobs that aren't already in the local database.

- `limit` defaults to `10`

Returns:
```ts
{
  restored: number;   // Entries newly indexed
  skipped: number;    // Entries already in DB
  total: number;      // Total blobs found on-chain
  namespace: string;
  owner: string;
}
```

### `health(): Promise<HealthResult>`

Check relayer health. Does not require authentication.

Returns: `{ status: string, version: string }`

### `getPublicKeyHex(): Promise<string>`

Return the hex-encoded public key for the current delegate key.

### Lower-level methods

| Method | Description |
|--------|-------------|
| `rememberManual({ blobId, vector, namespace? })` | Register a pre-uploaded blob ID with a pre-computed vector |
| `recallManual({ vector, limit?, namespace? })` | Search with a pre-computed query vector (returns blob IDs, no decryption) |
| `embed(text)` | Generate an embedding vector for text (no storage) |

---

## MemWalManual

```ts
import { MemWalManual } from "@mysten-incubation/memwal/manual";
```

Manual client flow — embed locally, SEAL encrypt locally, send encrypted payload + vector to relayer.

### Config (extends MemWalConfig)

| Field | Required | Notes |
|---|---|---|
| `embeddingApiKey` | Yes | OpenAI/OpenRouter-compatible embedding key |
| `embeddingApiBase` | No | Default: `https://api.openai.com/v1` |
| `embeddingModel` | No | Default: `text-embedding-3-small` |
| `packageId` | Yes | MemWal package ID on Sui |
| `registryId` | Yes | AccountRegistry shared object ID on Sui |
| `suiPrivateKey` or `walletSigner` | One required | Local keypair or connected wallet |
| `suiNetwork` | No | Default: `mainnet` |

---

## withMemWal (AI Middleware)

```ts
import { withMemWal } from "@mysten-incubation/memwal/ai";
```

Wraps a Vercel AI SDK model with automatic memory recall and save.

```ts
import { openai } from "@ai-sdk/openai";
import { streamText } from "ai";
import { withMemWal } from "@mysten-incubation/memwal/ai";

const model = withMemWal(openai("gpt-4o"), {
  key: "<your-delegate-key>",
  accountId: "<your-account-id>",
  serverUrl: "https://relayer.memory.walrus.xyz",
  namespace: "chat",
  maxMemories: 5,
  autoSave: true,
  minRelevance: 0.3,
});

const result = streamText({
  model,
  messages: [{ role: "user", content: "What do you remember about me?" }],
});
```

**Before generation:**
- Reads the last user message
- Runs `recall()` against MemWal
- Filters by minimum relevance (`minRelevance`, default `0.3`)
- Injects matching memories into the prompt as a system message

**After generation:**
- Optionally runs `analyze()` on the user message (fire-and-forget)
- Saves extracted facts asynchronously

Options (extends MemWalConfig):

| Option | Default | Description |
|--------|---------|-------------|
| `maxMemories` | `5` | Max memories to inject per request |
| `autoSave` | `true` | Auto-save new facts from conversation |
| `minRelevance` | `0.3` | Minimum similarity score (0–1) to include a memory |
| `debug` | `false` | Enable debug logging |

---

## Account Management

```ts
import {
  createAccount,
  addDelegateKey,
  removeDelegateKey,
  generateDelegateKey,
} from "@mysten-incubation/memwal/account";
```

| Function | Description |
|----------|-------------|
| `generateDelegateKey()` | Generate a new Ed25519 keypair (returns `privateKey`, `publicKey`, `suiAddress`) |
| `createAccount(opts)` | Create a new MemWalAccount on-chain (one per Sui address) |
| `addDelegateKey(opts)` | Add a delegate key to an account (owner only) |
| `removeDelegateKey(opts)` | Remove a delegate key from an account (owner only) |

`addDelegateKey` and `removeDelegateKey` require the shared `registryId` alongside the package and account IDs.

---

## Utility Functions

```ts
import { delegateKeyToSuiAddress, delegateKeyToPublicKey } from "@mysten-incubation/memwal";
```

| Function | Description |
|----------|-------------|
| `delegateKeyToSuiAddress(privateKeyHex)` | Derive the Sui address from a delegate private key |
| `delegateKeyToPublicKey(privateKeyHex)` | Get the 32-byte public key from a delegate private key |

---

## Configuration Reference

### MemWalConfig

Used by `MemWal.create(config)` and `withMemWal(model, options)`.

| Field | Required | Notes |
|---|---|---|
| `key` | yes | Delegate private key in hex |
| `accountId` | yes | MemWalAccount object ID on Sui |
| `serverUrl` | no | Relayer URL. Default: `https://relayer.memory.walrus.xyz` |
| `namespace` | no | Default memory boundary. Default: `"default"` |

### MemWalManualConfig

Used by `MemWalManual.create(config)`.

| Field | Required | Notes |
|---|---|---|
| `key` | yes | Delegate private key in hex |
| `serverUrl` | no | Relayer URL |
| `embeddingApiKey` | yes | OpenAI/OpenRouter-compatible embedding key |
| `embeddingApiBase` | no | Default: `https://api.openai.com/v1` |
| `embeddingModel` | no | Default: `text-embedding-3-small` |
| `packageId` | yes | MemWal package ID on Sui |
| `registryId` | yes | AccountRegistry shared object ID on Sui |
| `accountId` | yes | MemWalAccount object ID |
| `namespace` | no | Default namespace |
| `suiPrivateKey` | one of two | Use for local signing |
| `walletSigner` | one of two | Use a connected browser wallet instead |
| `suiNetwork` | no | `testnet` or `mainnet`. Default: `mainnet` |

### Managed Relayer Endpoints

| Network | Relayer URL |
|---|---|
| **Production** (mainnet) | `https://relayer.memory.walrus.xyz` |
| **Staging** (testnet) | `https://relayer-staging.memory.walrus.xyz` |

---

## Relayer Overview

The relayer is the backend that turns SDK calls into memory operations:

- **Authenticates requests** by verifying Ed25519 signatures against onchain delegate keys
- **Generates embeddings** using an OpenAI-compatible API (default: `text-embedding-3-small`, 1536 dimensions)
- **Encrypts and decrypts** data through the SEAL sidecar
- **Uploads and downloads** encrypted blobs to/from Walrus
- **Stores and searches vectors** in PostgreSQL (pgvector)
- **Orchestrates flows** like `analyze` (LLM fact extraction) and `ask` (memory-augmented Q&A)
- **Restores memory spaces** by querying onchain blobs, decrypting, and re-indexing

### Authentication Headers

| Header | Description |
|--------|-------------|
| `x-public-key` | Hex-encoded Ed25519 public key (32 bytes) |
| `x-signature` | Hex-encoded Ed25519 signature (64 bytes) |
| `x-timestamp` | Unix timestamp in seconds (5-minute validity window) |
| `x-nonce` | UUID v4 nonce for replay protection |
| `x-account-id` | MemWalAccount object ID hint included in the canonical signature by official SDKs |
| `x-seal-session` | Exported SEAL SessionKey for TypeScript and Python relayer-mode decrypt flows |
| `x-delegate-key` | Legacy decrypt credential; deprecated where `x-seal-session` is supported |

Signature format: `{timestamp}.{method}.{path_and_query}.{body_sha256}.{nonce}.{account_id}`

### Relayer API Routes

| Method | Route | Description |
|--------|-------|-------------|
| `GET` | `/health` | Service health check and compatibility metadata (no auth) |
| `GET` | `/version` | Relayer/API compatibility metadata (no auth) |
| `POST` | `/api/remember` | Store text as encrypted memory |
| `POST` | `/api/recall` | Semantic search for memories |
| `POST` | `/api/remember/manual` | Register client-encrypted payload |
| `POST` | `/api/recall/manual` | Search with pre-computed vector |
| `POST` | `/api/analyze` | Extract facts via LLM, store each |
| `POST` | `/api/ask` | Memory-augmented Q&A |
| `POST` | `/api/restore` | Rebuild missing index entries |

---

## OpenClaw / NemoClaw Plugin

### Installation

```bash
openclaw plugins install @mysten-incubation/oc-memwal
```

### Configuration

Set the delegate key as an environment variable:

```bash
export MEMWAL_PRIVATE_KEY="your-64-char-hex-key"
```

Add to `~/.openclaw/openclaw.json`:

```json
{
  "plugins": {
    "slots": { "memory": "oc-memwal" },
    "entries": {
      "oc-memwal": {
        "enabled": true,
        "config": {
          "privateKey": "${MEMWAL_PRIVATE_KEY}",
          "accountId": "0x...",
          "serverUrl": "https://relayer.memory.walrus.xyz"
        }
      }
    }
  }
}
```

### Plugin Options

| Option | Default | Description |
|--------|---------|-------------|
| `autoRecall` | `true` | Inject relevant memories before each turn |
| `autoCapture` | `true` | Extract and store facts after each turn |
| `maxRecallResults` | `5` | Max memories injected per turn |
| `minRelevance` | `0.3` | Relevance threshold (0-1) |
| `captureMaxMessages` | `10` | Messages to analyze for facts |
| `defaultNamespace` | `"default"` | Memory scope for the main agent |

### Lifecycle Hooks

| Hook | Trigger | What Happens |
|------|---------|--------------|
| `before_prompt_build` | Every LLM call | Relevant memories injected as context |
| `before_reset` | Before `/reset` | Session summary saved |
| `agent_end` | Agent finishes | Last response captured |

---

## Environment Variables (Self-Hosting)

### Required

| Variable | Notes |
|---|---|
| `DATABASE_URL` | PostgreSQL connection string. `pgvector` must already exist |
| `MEMWAL_PACKAGE_ID` | Sui package ID |
| `MEMWAL_REGISTRY_ID` | Onchain registry object ID |

`SEAL_SERVER_CONFIGS` and `SEAL_KEY_SERVERS` are optional overrides for encrypt/decrypt. Prefer `SEAL_SERVER_CONFIGS` for custom committees.
If neither SEAL variable is set, the sidecar uses built-in defaults for `SUI_NETWORK`: Mysten's initial committee aggregator on `testnet`, and the legacy independent key server pair on `mainnet` until an official mainnet committee aggregator is available. Deployments with existing memories encrypted by the previous testnet independent key server defaults should pin `SEAL_KEY_SERVERS=0x73d05d62c18d9374e3ea529e8e0ed6161da1a141a94d3f76ae3fe4e99356db75,0xf5d14a81a982144ae441cd7d64b09027f116a468bd36e7eca494f750591623c8` until the data is migrated or re-encrypted.

### Usually Required

| Variable | Notes |
|---|---|
| `SERVER_SUI_PRIVATE_KEY` | Primary server key |
| `OPENAI_API_KEY` | Embedding and fact-extraction provider |

### Package Contract IDs

Staging (Testnet):
```
MEMWAL_PACKAGE_ID=0xcf6ad755a1cdff7217865c796778fabe5aa399cb0cf2eba986f4b582047229c6
MEMWAL_REGISTRY_ID=0xe80f2feec1c139616a86c9f71210152e2a7ca552b20841f2e192f99f75864437
```

Production (Mainnet):
```
MEMWAL_PACKAGE_ID=0xcee7a6fd8de52ce645c38332bde23d4a30fd9426bc4681409733dd50958a24c6
MEMWAL_REGISTRY_ID=0x0da982cefa26864ae834a8a0504b904233d49e20fcc17c373c8bed99c75a7edd
```

---

## Links

- **Docs**: https://docs.wal.app/walrus-memory/getting-started/what-is-memwal
- **SDK on npm**: https://www.npmjs.com/package/@mysten-incubation/memwal
- **GitHub**: https://github.com/MystenLabs/MemWal
- **Dashboard**: https://memory.walrus.xyz