Memory Encryption

Organic LLM encrypts sensitive chat and summary data at rest. This page explains the problem we solved, the research and options we weighed, and the architecture we implemented.

Sensitive text also lives in two different places at rest: thread messages and related summaries in our primary database (Supabase)—the focus of most of this article—and long-term memory in a separate vector memory backend (Mem0 on Qdrant) when field encryption is enabled. For a concise overview of how we protect stored memory text and the product boundary around it, see How we protect your memories.

Problem statement

Conversations and AI-generated summaries are high-sensitivity data. Storing them in plaintext in a database exposes users to risk if the database is compromised, leaked, or accessed by admins. We wanted application-layer encryption so that even with database access, attackers see only ciphertext. The solution had to fit our Next.js and Supabase stack, avoid risky migrations, and stay compatible with future key rotation or KMS.

Research

We investigated how to design a practical, production-ready encryption layer for Organic LLM.

Goal of the research

The goal was to protect stored AI conversation data and summaries while integrating cleanly with the existing stack, avoiding high-risk migrations, and remaining compatible with future improvements like KMS and key rotation.

Survey of security practices in major AI applications

We looked at how leading AI systems secure chat data: ChatGPT, Claude, Gemini, Cursor, Notion AI, Perplexity.

LayerTypical technology
Transport encryptionTLS 1.2 / TLS 1.3
Storage encryptionAES-256
Key managementKMS / HSM

Most rely on transport and disk encryption but rarely use application-layer encryption—so plaintext prompts are often stored in databases. That gap meant application-layer encryption would significantly strengthen Organic LLM.

Encryption model evaluation

Option A — Database-only encryption

Protects physical disk.
Does not protect against database compromise or admin access.
Insufficient.

Option B — Application-layer encryption

Encrypt before writing to the DB.
Protects against DB leaks.
Common in privacy-focused apps; adds moderate complexity.
Best approach.

Encryption algorithm research

Option A — AES-256-GCM

Hardware-accelerated, built into Node.js.
Authenticated encryption (AEAD), widely audited.
The initialization vector, which varies ciphertext per encryption, must be unique and correctly generated each time.
Chosen for Node, built-in primitives, fewer dependencies.

Option B — XChaCha20-Poly1305

Forgiving nonce model, excellent design.
Requires external library; slower on systems with AES acceleration.
Good alternative but unnecessary for this rollout.

Key management research

Option A — Single global key

One compromise exposes all users.
Rejected.

Option B — HKDF-derived per-user keys

Root secret → HKDF → user key.
Isolates user data, no extra key storage.
Root compromise still affects all.
Chosen for near-term.

Option C — Per-user stored DEKs (KMS)

Strong isolation.
Requires key storage and KMS.
Future improvement.

Data sensitivity analysis

Sensitive

Message content, thread summaries (raw conversations, summaries, insights), and long-term memory text stored for retrieval across sessions.

Non-sensitive

Short excerpts, thread titles, timestamps, IDs.

Conclusion: Field-level encryption.

Safe deployment strategy

Option A — Full migration

Encrypt all rows at once.
Downtime and risk.
Rejected.

Option B — Mixed-mode

Rows may be plaintext or enc:v1: ciphertext.
Decrypt when prefix present; otherwise treat as plaintext.
Zero downtime, gradual rollout.
Chosen.

Ciphertext versioning

Self-describing format: a versioned prefix, key id, then initialization vector (IV), tag, and ciphertext. Supports algorithm upgrades, key rotation, and mixed data.

Logging and operational security

Real-world incidents show logs often leak prompts and conversations. We log only metadata (message ID, token counts, model, timings) and never plaintext.

Integrity protection

AES-GCM provides ciphertext authentication. We also use AAD (additional authenticated data) to bind user_id, thread_id, and field_name so ciphertext cannot be copied between records.

Outcome

The design provides protection against database compromise, authenticated integrity, user-scoped key compartmentalization, safe rollout without migration risk, and compatibility with future key rotation and KMS—while staying simple enough to implement in the current stack.

Research accelerated with ChatGPT Atlas.


Design space (options we weighed)

Implemented solution

What is encrypted

WhatWhen
Message contentEncrypted on insert; decrypted on read
Thread summaryEncrypted when writing; decrypted when building context
Conversation summaryEncrypted in update; decrypted when reading

Not encrypted: Primary keys, timestamps, role, thread titles, short excerpts, etc.

Long-term memory (Mem0 / Qdrant)

In addition to the message and summary fields above, Organic LLM can encrypt sensitive string fields on memory records before they are written to the vector memory database (Qdrant via Mem0). When configured, a server-side wrapper encrypts those fields on insert/update and decrypts them on search and reads; embedding vectors stay available for semantic search and are not encrypted at that layer.

  • Algorithm: AES-256-GCM with versioned deployment key material (separate wire format from the enc:v1: message payload style described above).
  • Scope: Readable memory text at rest; not a replacement for the authenticated memory APIs, rate limits, or TLS described on the companion page.
  • More detail: How we protect your memories.

Server-only

The crypto module is server-only and is never imported by client code.

Key derivation and AAD

  • Root secret: Key derivation and AAD use a root secret (supplied at deploy time). Optional: an active key id and a key registry for rotation or multiple keys.
  • Per-user key: HKDF-SHA256 derives a 32-byte key per user from the root secret (with salt and a fixed info string). No per-user keys are stored.
  • AAD: Ciphertext is bound to user id, thread id, and field name so it cannot be reused in another context.

Payload format and legacy

  • Encrypted value: enc:v1:<keyId>:<ivBase64>:<tagBase64>:<ciphertextBase64>
  • Legacy: Values not starting with enc: are returned as plaintext.

Where it runs

  • Crypto module: Encrypt/decrypt API and key derivation; server-only.
  • Data layer: Encrypt/decrypt messages and both summary fields; resolves thread owner first.
  • LLM layer: Decrypt/encrypt summary text when building or updating context.

How the layers fit together:


Final architecture and security properties

End-to-end pipeline:

User
Next.js server
  • Message sent to external LLM; response returned to user via TLS
  • In parallel: HKDF-derived user keys → AES-256-GCM encryption → send to Supabase
Database
  • Supabase (AES-256 at rest)

Animated flow (message and encrypted payloads move through TLS; boundaries static):

Hello
Hello
Hi
there
!
UserAES-256-GCM encryption
LLMAES-256-GCM encryption
User
Hi there!
TLS
Next.js server
Hi there!
TLS
DatabaseSupabase
TLS
Cloud LLM

Ciphertext format: A versioned prefix, key id, then initialization vector (IV), tag, and ciphertext (e.g. enc:v1:<keyId>:<iv>:<tag>:<ciphertext>).

Encrypted fields (Supabase path): Message content, thread summary, conversation summary. Long-term memory text may additionally be encrypted in the vector memory store as described above.

Security properties achieved

  • Database compromise: Attackers see ciphertext only.
  • Row swapping / tampering: Detected by AEAD authentication tag.
  • Partial compromise: User-scoped keys reduce blast radius.
  • Operational rollout: Mixed plaintext/encrypted rows supported.

Why this fits Organic LLM

Organic LLM stores personal conversations, long-term AI memory, research notes, and summaries. This architecture protects those while still allowing AI reasoning, efficient queries, and gradual system evolution.