Skip to main content

πŸ” Encrypted Memory with Memwal

Copy memwal.mjs below into your own project and run it with npx 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-lighthouse if you want pinned IPFS mirrors).
  • Credentials from the dashboard - per-network, they do not interoperate:
NetworkDashboardRelayer
testnet (start here)https://staging.memory.walrus.xyzhttps://relayer-staging.memory.walrus.xyz
mainnethttps://memory.walrus.xyzhttps://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​

SymptomFix
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 statusWrong MEMWAL_SERVER_URL / network, or relayer down - recall will fail, local list still works.
pinned: false, pinPending: trueLighthouse key missing or pin failed - run repinPending() after fixing the key.
analyze() not supportedClient without analyzeAndWait - upgrade @mysten-incubation/memwal.

Next steps​