The two layers
| chat-relayer (cross-session) | node (intra-session) | |
|---|---|---|
| Scope | Across conversations, long-term | Within one conversation, short-term |
| What it remembers | User facts (“user prefers Rust”, “user is in Bengaluru”) | Verbatim conversation turns of this session |
| Recall trigger | Every turn of every session | Every turn of the current session |
| Selection | Embedding similarity over all memories | Sliding window from session start |
| Storage shape | pgvector (similarity index) + Walrus blobs (fact content) | Encrypted session blob on Walrus, keyed by session_id |
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.
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.
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 asmemwal_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