← FEATURES
ENCRYPTION AT REST

The key you hold

Every key that can decrypt your memories exists only inside a live request, derived from a credential you already carry. Not a promise not to look. An inability to.

THE WHOLE PICTURE
Diagram of Muninn's key hierarchy: five credentials each derive a KEK that unwraps one per-account DEK; the DEK wraps a per-record content key, derives an HMAC tag key and a vector-rotation key, and feeds KV, D1 and Vectorize. Below, a per-team TEK is wrapped once per member and adds a fifth field to the record envelope.

The full hierarchy · static SVG · open the file

01 · FOUR KEYS
01

Four keys, and only one of them is yours to lose

The names in the diagram, said plainly.

DEK

Your account key. 256 bits of randomness, minted once and never changed for the life of the account. It is never stored in a form anything can use — only as wrapped copies.

KEK

Derived, on the spot, from the credential the request is already carrying: your password, your API key, your recovery code, your browser session. It exists to unwrap the DEK and then it is gone.

CK

A fresh random key for every single record. The memory body is encrypted under its own CK; the CK is wrapped under your DEK. This is what makes sharing cheap later.

TEK

One key per team, wrapped once for each member under that member’s own DEK. Nobody hands anybody their personal key to work together.

The consequence worth sitting with: there is no master key, and no operator key. A dump of the database is a pile of ciphertext, wrapped keys, and salts. Nothing in it opens anything else in it.

The one thing you can genuinely lose is the last credential that can unwrap your DEK. That is what the recovery code is for, and why it is worth keeping somewhere real.

02 · THE ENVELOPE
02

What is actually written down

One encrypted record is one string, in four fields.

v1 . wrappedCK . iv . ciphertext + GCM tag

AES-256-GCM, with the record’s own identity bound into the ciphertext as additional authenticated data. Move the bytes into another user’s slot and the decryption does not quietly return the wrong thing — it fails.

Tags, without the tag names

Search has to keep working, so the parts that make it work get their own treatment. A tag key is derived from your DEK and each tag is stored as an HMAC — the index sees opaque hex, never the word. The readable list of your own tag names lives as one more encrypted blob, decrypted in memory for the length of a request.

Vectors, in a basis nobody else knows

Semantic search means embeddings, and embeddings from a public model can be inverted back into approximate text. So before a vector is stored it is rotated by a transform derived from your DEK — an exactly orthogonal one, so distances and rankings are untouched and results are identical. The transform is never stored anywhere; it is regenerated from your key on each request. An exfiltrated index inverts to noise.

03 · TEAMS
03

Sharing, without giving anyone your key

A team key that adds one field, and dies the moment someone leaves.

A team gets one TEK. It is wrapped once per member, under that member’s own DEK, so each member fetches the team key using the key they already have. When you share a memory, its content key gets a second wrap — under the TEK — added to the same envelope as a fifth field.

v1 . wrappedCK . iv . ciphertext + GCM tag . wrappedCK under TEK

The body is never re-encrypted, and never copied. Roughly eighty bytes are added. A teammate reads it by unwrapping the TEK with their own key, unwrapping the content key from that fifth field, and decrypting the original record in place. Shared tags and shared vectors get the same treatment under the team key, so what is shared stays findable without becoming readable to the server.

Remove a member and the team rekeys: a fresh TEK, re-wrapped for everyone who stays, and a version bump that makes it live. The copy the departed member could unwrap is simply dead.

And if nobody has shared anything with you, none of this runs. The team path is gated on there actually being someone else’s shared content to find.

04 · THE HONEST EDGES
04

Where the line actually is

The guarantee is a precise one, and precision is worth stating in both directions.

What you get is an operator who holds no key. Your DEK is unwrapped inside the request that carries your credential, used, and dropped. There is no copy of it, no master key standing in for it, and no path by which we can read your memories — not for support, not for a subpoena served on the storage, not for anyone who walks off with a database dump.

What it is not is end-to-end encryption, and the reason is recall. Finding a memory by meaning requires an embedding of what it says, and that embedding is computed at the moment you save. Encrypting on your device instead would mean nothing could ever be indexed, and Muninn would become a filing cabinet you have to remember the name of — which is the product we deliberately did not build. So plaintext exists for the length of one request, in memory, and is never written down in the clear: not the body, not the preview, not the tag.

Unattended work is opt-in and explicit. A scheduled mirror to your own Drive or GitHub cannot read a thing unless you have connected one, which creates a separate wrapped copy of your key for exactly that purpose — and disconnecting removes it.

Metadata is not content. Sizes, timestamps and record counts are visible in a way ciphertext is not.

05 · THE DIAGRAM ITSELF
Made in Muninn · shared with one link

The plate at the top of this page was drawn by an AI working inside Muninn, stored there as an artifact, and published with a single link — which is the feature it happens to be describing. There is an animated version that builds the whole hierarchy in ten seconds, and it is live at a Muninn address, not on this site:

Same store, same keys, same envelope described above. An artifact is a memory that renders; publishing one gives it a public address without giving anything else away.