π Encrypted Memory with Memwal
Copy
memwal.mjsbelow into your own project and run it withnpx tsx memwal.mjs.
memwal.mjs - the full fileβ
// memwal.mjs β encrypted: MemwalMemory via relayer. Needs MEMWAL_PRIVATE_KEY + MEMWAL_ACCOUNT_ID.
import 'dotenv/config'
import { MemwalMemory } from '@lighthouse-ai/engine-memwal'
const memory = await MemwalMemory.fromEnv()
console.log('status (before):', await memory.status())
// 1. STORE β relayer embeds + encrypts + uploads; we CID-pin the record.
const stored = await memory.remember('User prefers dark mode.', { tags: ['preference'] })
console.log('remembered:', stored)
// { id, blobId, cid, pinned, gatewayUrl?, walrusUrl, network, namespace }
// 2. RETRIEVE β semantic query has zero word overlap on purpose.
console.log('recall:', await memory.recall('what theme does the user like?'))
console.log('get:', await memory.get(stored.id))
console.log('list:', await memory.list())
console.log('status:', await memory.status())
// 3. EXTRAS (memwal-only)
console.log('verify:', await memory.verify(stored.id))
console.log('blobIds:', await memory.blobIds(stored.id))
console.log('repinPending:', await memory.repinPending())
// 4. ANALYZE (opt-in: MEMWAL_ANALYZE=1 β extracts discrete facts via the relayer)
if (process.env.MEMWAL_ANALYZE === '1') {
console.log('analyze:', await memory.analyze('Alice moved to Lisbon in June and prefers dark mode.', {}))
}
// NOTE: forget() is intentionally NOT run β the encrypted blob stays on
// Walrus until its storage period lapses and recall may still surface it
// as indexed:false. Uncomment to exercise it:
// console.log('forget:', await memory.forget(stored.id))
npx tsx memwal.mjs
# MEMWAL_ANALYZE=1 npx tsx memwal.mjs # also run the analyze() demo
Why memwal?β
Memwal is a full engine, not just storage: the relayer embeds, SEAL-encrypts, uploads to Walrus, and serves vector search. You never configure a storage or embedder, never call flush(), and every remember is encrypted before it touches Walrus. The trade-off is a hosted dependency (relayer + delegate key) and plaintext IPFS mirrors when you opt into pinning.
Use it when encryption-by-default and zero batching logic outweigh the DIY control of the batched engine.
Prerequisitesβ
- Any batched tutorial completed (so the no-
flush()difference lands). - The memwal engine installed:
npm install @lighthouse-ai/engine-memwal dotenv(add@lighthouse-ai/store-lighthouseif you want pinned IPFS mirrors). - Credentials from the dashboard - per-network, they do not interoperate:
| Network | Dashboard | Relayer |
|---|---|---|
testnet (start here) | https://staging.memory.walrus.xyz | https://relayer-staging.memory.walrus.xyz |
mainnet | https://memory.walrus.xyz | https://relayer.memory.walrus.xyz |
MEMWAL_PRIVATE_KEY=<hex-delegate-key>
MEMWAL_ACCOUNT_ID=0x...
MEMWAL_NETWORK=testnet # testnet (staging) or mainnet
MEMWAL_NAMESPACE=demo-memwal # optional
LIGHTHOUSE_API_KEY=lh_... # optional: pins CID mirrors (public). If missing, local CIDs only.
MEMWAL_ANALYZE=1 # optional: also run analyze() demo
Step 1 - Connect with one callβ
memwal.mjs calls MemwalMemory.fromEnv() and logs status() first. fromEnv() resolves the network, builds the relayer client (MemWal.create({ key, accountId, serverUrl, namespace })), and picks the pinner: Lighthouse when LIGHTHOUSE_API_KEY is set (and MEMWAL_IPFS_PIN != off), else local-CID. status() before any writes shows zero memories, the relayer health (ok (vβ¦) or unreachable), and which IPFS mode you are in β check this first when debugging.
Step 2 - Store (direct write, no flush)β
memwal.mjs remembers once and gets { id, blobId, cid, pinned, gatewayUrl?, walrusUrl, network, namespace } back. There is deliberately no flush() call β the relayer already embedded, encrypted, and uploaded. blobId is the SEAL-encrypted Walrus blob; cid is your IPFS mirror (pinned/public with a Lighthouse key, locally computed otherwise); walrusUrl is the aggregator fetch for the encrypted bytes.
Mental-model shift from batched: remember latency is relayer latency (tune with MEMWAL_REMEMBER_TIMEOUT_MS, default 60s), and there is no pending queue to lose.
Step 3 - Retrieve semanticallyβ
memwal.mjs then retrieves with the same four calls. Recall runs in the relayer and returns { blobId, content, distance, score (1βdistance), walrusUrl, indexed } enriched with your local tags/CIDs. indexed: false means the relayer knows a blob this machine never indexed (written elsewhere) β still readable, just tag-less locally.
Step 4 - Use the memwal-only extrasβ
memwal.mjs exercises verify() (gateway byte-compare if pinned, else local re-hash), blobIds() ({ memwalBlobId, recordBlobIds } for Sui tooling), and repinPending() ({ repinned, remaining }).
verify() answers is what IPFS serves byte-identical to my local record? - the integrity check to run before trusting a mirror. blobIds() bridges to the Sui side. repinPending() clears the pendingPins counter in status() after transient Lighthouse outages.
Step 5 - Extract facts with analyze() (opt-in)β
Run with MEMWAL_ANALYZE=1 β memwal.mjs calls memory.analyze('Alice moved to Lisbon in June and prefers dark mode.', {}) and gets { factCount: 2, succeeded: 2, failed: 0, memories: [β¦] } back.
The relayer splits free text into discrete facts and stores each as its own memory (tagged ['analyzed'], each with its own CID). Use it for ingesting notes, transcripts, or onboarding docs - one paragraph in, many searchable memories out.
Step 6 - Why forget() is not in the demoβ
memwal.mjs leaves forget() commented out on purpose:
forget() removes the local copy and unpins the mirror, but the relayer has no delete API - the encrypted blob stays on Walrus until its storage period lapses and may still surface in recall as indexed: false. Uncomment the line to exercise it, but understand the guarantee: local + IPFS gone now, Walrus gone later.
Also plaintext caveat: memwal blobs are SEAL-encrypted, but pinned CID mirrors (and all snapshots) are readable by anyone with the CID. Do not store secrets in mirrored records - set MEMWAL_IPFS_PIN=off when mirrors must not exist.
Troubleshootingβ
| Symptom | Fix |
|---|---|
MEMWAL_PRIVATE_KEY and MEMWAL_ACCOUNT_ID are required | .env in repo root, not shell - and matching network (testnet creds fail on mainnet). |
relayer: unreachable in status | Wrong MEMWAL_SERVER_URL / network, or relayer down - recall will fail, local list still works. |
pinned: false, pinPending: true | Lighthouse key missing or pin failed - run repinPending() after fixing the key. |
analyze() not supported | Client without analyzeAndWait - upgrade @mysten-incubation/memwal. |
Next stepsβ
- Cross-Device Backup & Restore - snapshot + pointer flow for memwal (same
snapshotIndex/rebuildLocalpair). - Memwal Engine reference -
restore(), snapshots, networks. - MCP memwal tools -
memory_analyze,memory_verify,memory_blob_idsfor agents.