π Memwal Engine
MemwalMemory (@lighthouse-ai/engine-memwal) is the relayer engine: embedding, SEAL encryption, Walrus upload, and vector search all happen in the relayer. You keep tags, CIDs, and blob IDs locally. Every remember goes straight to the network - there is no batching and no flush().
import { MemwalMemory } from '@lighthouse-ai/engine-memwal'
const memory = await MemwalMemory.fromEnv() // MEMWAL_PRIVATE_KEY + MEMWAL_ACCOUNT_ID
await memory.remember('User prefers dark mode.', { tags: ['preference'] })
await memory.recall('what theme does the user like?')
Setup - MemwalMemory.fromEnv(env?)β
| Env var | Required | Purpose |
|---|---|---|
MEMWAL_PRIVATE_KEY | Yes | Hex delegate key from the dashboard |
MEMWAL_ACCOUNT_ID | Yes | 0x... account id from the dashboard |
MEMWAL_NETWORK | No (mainnet) | mainnet or testnet (staging relayer; creds are per-network) |
MEMWAL_SERVER_URL | No | Override relayer URL (defaults per network) |
MEMWAL_NAMESPACE | No (default) | Relayer + local namespace |
MEMWAL_AGENT | No (agent) | Recorded on every memory |
MEMWAL_MEMORY_DIR | No (./.memory-sdk/memwal) | Local index dir |
MEMWAL_REMEMBER_TIMEOUT_MS | No (60000) | rememberAndWait timeout |
LIGHTHOUSE_API_KEY | No | Pins a public IPFS mirror of each record |
MEMWAL_IPFS_PIN | No (pin) | off skips IPFS pinning (local CIDs only) |
Get credentials at https://memory.walrus.xyz (mainnet) or https://staging.memory.walrus.xyz (testnet). Testnet and mainnet credentials do not interoperate.
Or construct manually:
import { MemwalMemory, resolveNetwork, LighthouseIpfsPinner } from '@lighthouse-ai/engine-memwal'
const memory = new MemwalMemory(client, { // client: MemwalClient (e.g. MemWal.create({ key, accountId, serverUrl, namespace }))
network: resolveNetwork('mainnet'),
namespace: 'demo',
agent: 'support-bot',
dataDir: './.memory-sdk/memwal',
pinner: new LighthouseIpfsPinner(process.env.LIGHTHOUSE_API_KEY!),
rememberTimeoutMs: 60_000,
})
remember(content, opts?)β
const res = await memory.remember('Customer ACME is on enterprise.', { tags: ['customer'] })
// { id, blobId, cid, pinned, gatewayUrl?, walrusUrl, network, namespace }
The relayer embeds + SEAL-encrypts + uploads to Walrus and returns { id, blob_id }. The SDK then pins a canonical JSON record to IPFS (Lighthouse if LIGHTHOUSE_API_KEY is set, otherwise a locally computed CID) and appends it to <network>.<namespace>.index.json. If pinning fails, the record is kept with pinPending: true - retry with repinPending().
LIGHTHOUSE_API_KEY mirrors are plaintext; memwal blobs on Walrus are SEAL-encrypted.
recall(query, opts?)β
const matches = await memory.recall('how do I escalate for ACME?', {
tags: ['customer'], // applied locally after the relayer returns hits
limit: 5, // default 5
maxDistance: 1.2, // optional: drop matches with distance >= this value
})
// [{ blobId, content, distance, score, id?, tags?, cid?, pinned?, gatewayUrl?, walrusUrl, indexed }]
- Search runs in the relayer; the SDK over-fetches (
max(limit*4, 20)) when tag filters are present, then filters locally. scoreis1 β distanceclamped to0..1.indexed: falsemeans the relayer knows the blob but this machine has no local record (written elsewhere, never snapshotted here).
analyze(text, opts?)β
Extract discrete facts from free text via the relayer; each fact becomes its own memory with its own IPFS CID.
const out = await memory.analyze('Alice moved to Lisbon in June and prefers dark mode.', {
occurredAt: '2026-06-15T00:00:00.000Z', // optional
})
// { factCount: 2, succeeded: 2, failed: 0, memories: [RememberResult, β¦] }
Facts are tagged ['analyzed']. Requires a client with analyzeAndWait - throws otherwise.
list(opts?)β
await memory.list({ limit: 20, tags: ['preference'] }) // newest first
// [{ id, blobId, cid, content, tags, createdAt, walrusUrl, gatewayUrl?, ... }]
Unlike batched, memwal list accepts tags natively.
get(ref)β
await memory.get('job-id-β¦') // by relayer id
await memory.get('bafβ¦') // by IPFS CID (local index, else gateway fetch)
await memory.get('walrus-blob-id') // by Walrus blob id
Falls back to a gateway fetch for pinned CIDs not in the local index. Throws for unknown refs with guidance to use recall() (semantic search works without a local record).
verify(ref)β
await memory.verify('job-id-β¦')
// pinned: { id, cid, pinned: true, verified: true, method: 'gateway-fetch' }
// unpinned: { id, cid, pinned: false, verified: true, method: 'local-hash' }
Pinned records are fetched from the gateway and compared byte-for-byte; unpinned records are re-hashed locally (computeRawCid).
forget(ref)β
await memory.forget('job-id-β¦')
// { removed: true, blobDeleted: true|false, note: 'Removed from the local indexβ¦' }
Removes the local copy and unpins the IPFS mirror. The relayer has no delete API - the SEAL-encrypted blob stays on Walrus until its storage period lapses and may still surface in recall() as indexed: false.
blobIds(ref)β
await memory.blobIds('job-id-β¦')
// { memwalBlobId: 'β¦', recordBlobIds: ['β¦'] }
The encrypted memwal blob plus Walrus-native blob IDs backing the pinned IPFS record (empty when unpinned).
restore(opts?)β
await memory.restore({ maxBlobs: 200 }) // { restored, skipped, total, calls }
Asks the relayer to rebuild missing vector-index entries from on-chain data. Pages exponentially (25 β 50 β β¦ β maxBlobs) until everything is inspected.
repinPending()β
await memory.repinPending() // { repinned: 2, remaining: 0 }
Retries IPFS pins for entries marked pinPending (see pendingPins in status()).
snapshotIndex() / rebuildLocal(snapshotCid)β
const snap = await memory.snapshotIndex()
// { cid, pinned, entries, gatewayUrl? }
await fresh.rebuildLocal(snap.cid) // { added, total }
Pins the entire local index as one memwal-index.<network>.<namespace>.json snapshot and merges it elsewhere. Snapshots are namespace- and network-checked, merge-only (known ids are skipped), and plaintext like pinned records.
status()β
await memory.status()
// {
// network: 'mainnet', relayerUrl: 'https://relayer.memory.walrus.xyz',
// relayer: 'ok (v1.2.3)', dashboardUrl: 'https://memory.walrus.xyz',
// namespace: 'demo', agent: 'agent',
// memories: 12, pinnedMemories: 10, pendingPins: 2,
// ipfs: 'lighthouse (pinned, publicly resolvable)' | 'local CIDs (computed, not pinned)',
// indexPath: '.memory-sdk/memwal/mainnet.demo.index.json'
// }
Networksβ
mainnet (default) | testnet | |
|---|---|---|
| Relayer | https://relayer.memory.walrus.xyz | https://relayer-staging.memory.walrus.xyz |
| Dashboard / credentials | https://memory.walrus.xyz | https://staging.memory.walrus.xyz |
| Sui fullnode / aggregator | mainnet | testnet |
Use resolveNetwork(name), walrusBlobUrl(config, blobId), and NETWORKS from the package when building URLs manually.