Memory That Only You Can Read: Encryption That Travels With Each Thought
Encryption lives at the level of each memory, not the whole database โ which is exactly what lets you share one thing without handing over everything
Imagine every memory you have in its own small locked box. NAOMS doesn't put a strong lock on the room and call your thoughts safe; it locks each box individually, and only your key opens them. The reason it works this way is the whole point of this piece: when each memory is its own box, you can hand one box to a friend without throwing open the whole room.
Most "encrypted at rest" stories end at locking the room โ the database file. You turn on disk encryption, the file is opaque to anyone who steals the laptop, and you call it a day. NAOMS does that too โ and then keeps going, because locking the room answers the wrong threat for a memory system.
The question a memory system has to answer isn't only "what if someone steals the disk." It's "what if I want to share this one memory with this one person โ without handing them the entire database, and without re-encrypting everything I own." Answering that forces a decision most systems never make: encryption has to live at the field level, not the file level.
That decision landed on 2026-03-24, and this piece is about why it's built the way it is.
For builders
Two layers, on purpose
NAOMS encrypts memory at two independent layers:
- Layer 1 โ whole-DB: the entire SQLite database file is encrypted at rest (SQLCipher / ChaCha20-family). Steal the file, get noise.
- Layer 2 โ per-field: inside that database, each individual memory's content is additionally encrypted with AES-256-GCM, under a key derived from the being's HD key tree.
The two layers are deliberately independent and complementary. Layer 1 locks the room. Layer 2 locks each box inside it โ defending each memory against everything else, including, crucially, the ability to share one memory without exposing the rest. You can open a single box, hand the contents (or a re-wrapped key) to someone you trust, and the other ten thousand boxes stay sealed. Locking only the room can't do that: once the room is open, every box in it is open too.
This is the architectural payoff of per-field. Sharing granularity is a property you have to design in at the encryption boundary, and NAOMS drew that boundary around the field.
The envelope
Every encrypted memory field is stored as a small, self-describing JSON envelope:
{
"iv": "<hex-encoded 12-byte IV>",
"ciphertext": "<hex-encoded encrypted content>",
"authTag": "<hex-encoded 16-byte authentication tag>",
"algorithm": "aes-256-gcm",
"keyPath": "m/808'/0'/1'/0'/0'"
}Four of those five fields are standard AEAD plumbing:
- iv โ a random 12-byte nonce, unique per encryption, never reused (nonce reuse under GCM is catastrophic; the design states the uniqueness requirement explicitly).
- ciphertext โ the AES-256-GCM-encrypted content.
- authTag โ the 16-byte GCM authentication tag. This is what makes it authenticated encryption: tamper with the ciphertext, the IV, or the tag, and decryption fails rather than silently returning garbage.
- algorithm โ pinned to
aes-256-gcm.
The fifth field is where it gets interesting.
keyPath: the envelope tells you how to open itself
Notice what the envelope does not contain: the key. It does not ship the key
taped to the box. It contains a derivation path โ m/808'/0'/1'/0'/0' โ
which is a coordinate into the being's HD key tree: instructions for which key
cuts this particular lock, useless to anyone who doesn't already hold the master
that the keys are cut from. Purpose 1 in that path marks the key as an encryption key; the AES key
is the first 32 bytes of the private key derived at that path.
This is the hinge of the whole design, and it's worth sitting with. The envelope carries everything needed to find the key except the secret that generates the tree. Given your mnemonic, the path tells you exactly which key to derive. Given someone else's envelope, the path tells you nothing useful, because you can't derive into a tree you don't have the seed for.
That property is what the design calls the self-access guarantee:
A being possessing their BIP-39 mnemonic can decrypt ALL their memories without any running service, vault process, or network connection.
mnemonic โ seed โ HD key tree โ derive at keyPath โ AES key โ decryptNo vault daemon. No key server. No network. The keyPath in each envelope contains all the metadata needed to re-derive the exact key, and the mnemonic is the only secret required. This is sovereignty made operational: even if every NAOMS process on earth stopped running tomorrow, you could still open your own memory with twenty-four words and an off-the-shelf crypto library.
(Premium) Deriving the key, step by step
The free section above gives you the shape. Here's the mechanism, in the order the code runs it.
1. Mnemonic โ seed. The 24-word BIP-39 mnemonic (256-bit entropy, English wordlist) is stretched into a seed via PBKDF2 (the BIP-39 standard). The seed is held in memory and zeroed when the key tree is destroyed; the mnemonic itself is shown once at generation and never persisted in plaintext.
2. Seed โ HD tree. SLIP-10 derives an Ed25519 key tree from the seed. NAOMS
uses @scure/bip32 for SLIP-10 (audited, no native deps) and bip39 for the
mnemonic side. Every component in a NAOMS path is hardened (the ' in
m/808'/0'/1'/0'/0') โ SLIP-10 requires hardened derivation for Ed25519, so
there's no non-hardened branch to misuse.
3. The path's meaning. The schema is
m/808'/{identityType}'/{purpose}'/{domain}'/{index}':
808'โ the NAOMS purpose constant (the tree's root namespace).identityType'โ human / agent / domain, etc.purpose'โ here1'marks an encryption key (signing keys use0').domain'andindex'โ partition + rotation coordinates.
For memory encryption in Phase 1 (what shipped on 03-24), domain and index
are pinned to 0: all memories encrypt under the same derived key, with a
different random IV per field. That's safe under GCM precisely because the IV is
fresh every time โ same key + unique nonce is the supported mode; same key +
repeated nonce is the failure you must never hit, which is why "unique per
encryption operation, never reused" is written into the envelope spec as a hard
requirement.
4. Key โ AES. The AES-256 key is the first 32 bytes of the private key
derived at that path. From there it's standard AES-256-GCM over the field content,
producing the (iv, ciphertext, authTag) triple the envelope stores.
What Phase 1 deliberately did not do
This is the part where a deep-dive earns its keep โ by being honest about the edges.
Single-domain in Phase 1. Because every memory uses domain 0 index 0,
Phase 1 has no per-domain key isolation and no forward secrecy within a
being's own store. The design names this directly: Phase 2 introduces
domain-partitioned keys (different memory categories under different domains) and
per-memory index rotation for forward secrecy. As of the 03-24 landing, those are
designed, not shipped โ a Phase-2 intent, not a current property. If you
read "key isolation" in the security-properties list, the cross-being isolation
(different mnemonics can't cross-decrypt) is real today; the cross-domain
isolation within one being is the Phase-2 part.
No revocation ceremony here. Per-field encryption is what enables selective sharing, but the full share-and-revoke story โ wrapping a memory's key for a recipient, then rotating to claw it back โ lives in the separate key-wrapping and revocable-access designs, not in the per-field encryption itself. And note: the standalone revocable-access work is cancelled โ standalone cryptographic revocation was explored and deliberately not built in this era. So if you're reaching for "and then I can un-share it with a key rotation," be precise: per-field encryption gives you the granularity that makes sharing safe; the standalone revocation mechanism was not shipped. Don't let the elegance of the envelope tempt you into describing a revoke flow that doesn't exist yet.
The security properties, audited honestly
From the encryption design, with the honest caveats attached:
- Authenticated encryption โ tampering with ciphertext / IV / authTag is detected (GCM tag). โ shipped.
- Random IV โ same plaintext โ different ciphertext each time. โ shipped.
- Cross-being key isolation โ different mnemonics cannot cross-decrypt. โ shipped (it falls out of the tree structure).
- Cross-domain key isolation โ different domains use different keys. Phase 2, not shipped on 03-24.
- Forward secrecy via index rotation โ Phase 2, not shipped.
That's the shape of an honest crypto deep-dive: the AEAD core and the self-access guarantee are real and load-bearing today; the partitioning and rotation are designed and dated to a later phase; and the revocation story you might assume follows from per-field encryption was, for the standalone item, cancelled.
Why this is the right boundary
Step back and the decision tree is clean. Whole-DB encryption is the cheap, strong defense against a stolen file, and NAOMS keeps it. But a memory system's real ambition โ share this thought with this person, and nothing else โ can only be expressed if encryption lives at the field, because that's the only boundary fine enough to share across.
So NAOMS pays the cost: a box per memory, a derivation path per box, a fresh nonce per write. In exchange it gets the property that matters most โ memory that is yours in the strongest available sense. Yours to open with nothing but your own words running. Yours to hand one box of, without spilling the rest. The crypto is standard; the boundary โ one lock per box, not one lock per room โ is the design.
Written by AI agents from real project logs; owned and edited by Mujo.