Skip to main content
Chat memory is arguably the most sensitive data in the whole system, so it follows the same “don’t trust a private DB” principle as routing receipts — via two separate Walrus-backed layers that solve two different problems.

The two layers

Intra-session continuity (node-side)

Within a single conversation, the node serving a turn needs the prior turns to keep the thread coherent — but a different node might serve the next turn, since the network doesn’t pin a client to one operator. Each node:
  • Fetches the session’s encrypted Walrus blob (keyed by session_id), decrypts it with a key only the client controls (session_key), and assembles the context window.
  • After the turn, re-encrypts and re-uploads, chaining the new blob to the previous one (prev_blob_id) — so a session’s history is a tamper-evident chain on Walrus, not a row in a database any single node operator can edit.
This means any node — not just whichever one served turn 1 — can pick up turn 2 with full continuity. Decentralized compute only works in practice because the state isn’t trapped on one machine either.

Cross-session memory (chat-relayer)

Separately, chat-relayer gives chat.pinaivu.ai users long-term memory across different conversations — the equivalent of “remember I prefer concise answers” persisting weeks later. This layer:
  • Stores the actual fact content as an encrypted blob on Walrus — same principle as routing receipts: the durable copy lives outside any single company’s private infrastructure.
  • Keeps only a pgvector embedding + the Walrus blob pointer in chat-relayer’s own Postgres — enough to do similarity search (“what’s relevant to this new message”) without the plaintext fact ever sitting in that database.
  • Derives each user’s encryption key via HKDF from a master secret, scoped per owner_address — so one user’s memories aren’t decryptable using another user’s key, even though they’re stored in the same blob network.
One honest caveat. The per-user key derivation in chat-relayer (MemoryCrypto) is explicitly a placeholder today, not a finished trust-minimization story: the master secret it derives from is relayer-held, not held by the user’s own wallet. That means, as it stands, the relayer operator could technically derive any user’s key — real user-controlled access (the eventual plan is wiring in actual Seal-style SessionKey export, gated by the user’s own wallet signature) hasn’t landed yet. The storage is already decentralized (Walrus, not a private DB) and tamper-evident, but the access control on top of it is still a centralized placeholder.

How the two layers combine in a single turn

These aren’t redundant — they answer different questions, and a real chat.pinaivu.ai turn uses both, in sequence:

Why both are needed, not just one

The intra-session layer alone would lose everything the moment a conversation ends — there’d be no “remember I’m a Rust engineer” carrying into next week’s chat. The cross-session layer alone would lose mid-conversation coherence — every node serving turn 2 of an active chat would need the verbatim turn-1 exchange, which isn’t the kind of thing you want to re-derive from a fact-extraction pass (it’s too granular, and a different node serving turn 2 still needs exact continuity, not a summarized fact). So: cross-session memory carries durable facts forward across conversations; intra-session memory carries verbatim recent context forward across nodes within one conversation — and a node assembling a prompt pulls from both, with the cross-session layer arriving as memwal_context injected by chat-relayer at dispatch time, since the node itself has no notion of “this user’s history with other sessions” at all. That boundary is intentional: it’s what lets any node serve any session without needing broader access to a user’s full history.

Auth scheme for chat-relayer

How a client signs requests to /v1/chat — no API key, Ed25519 over canonical bytes