LumoSQL v0.8 implements a very basic libsodium page-level encryption demo, with simple SQLite-style key management. This demonstrates encryption works. Now we need to think about how we use the mature encryption framework in LMDBv1.0 to do implement comprehensive encryption. LumoSQL has facilities we hope to use for the completely different concept of row-level encryption, but that is not addressed in this document.
What we need to do next:
- Interactive key prompt:
.lumo-keydot-command reading from/dev/ttyvia getpass(3);--key-fd=Nand--key-stdinflags. Production needs this; passphrase on the command line leaks to ps, history, and /proc/PID/cmdline. LUMO_KEYenvironment variable as a non-interactive non-arglist fallback. One getenv() call.- Per-page checksum hook (
mdb_env_set_checksum) as the integrity-only sibling of encryption. Faster, no key management, useful for users who want corruption detection without confidentiality. - Key rotation: a
lumo_rekeyPRAGMA. LMDB has no in-place rekey, so this means a clone-with-new-key pass. - Raw-hex key form
lumo_key="x'..bytes..'"for callers that have already done their own KDF or use a KMS. - Argon2id parameters: expose ops/mem cost as build defines, document the security/UX trade-off, consider SENSITIVE-class as a build option.
- Better error reporting: SQLITE_AUTH currently surfaces as the shell's generic "unable to open database, unknown error". One small shell.c edit gives a useful message.
- Decide what to do about
MDB_WRITEMAP: assert it cannot coexist with encryption, document, possibly disable the option for encrypted builds.
Things to note:
MDB_REMAP_CHUNKS is the main point. LMDB 0.9.x's whole-file mmap means there's no natural "page is read in" event — the kernel page-faults pages of the mmap'd file directly. v1.0 line gives up the single-mmap and pages live in chunk-sized mmap regions that get faulted in via explicit calls. Decryption happens in the chunk caching layer.
The cipher choice is delegated to the app. LMDB provides a callback and apps such as LumoSQL provide OpenSSL, libsodium etc as they wish.
AEAD by default in the reference impl. ChaCha20-Poly1305 is authenticated encryption. This means "did this page get corrupted on disk or attacked?" is answered, together with confidentiality.
Optional separate checksum hook. For users who want integrity but don't care about confidentiality (faster, and no key management), there's mdb_env_set_checksum() independently of mdb_env_set_encrypt(). ChaCha8 is plausibly the intended built-in checksum: it's much faster than ChaCha20, but we haven't thought about this yet.
Persistent MDB_ENCRYPT flag. The env file remembers whether it was created encrypted, so opening a non-encrypted env with a key (or vice versa) returns MDB_ENV_ENCRYPTION.
Performance implications The MDB_REMAP_CHUNKS mode necessarily costs throughput vs a single-mmap LMDB — every page access goes through a chunk-cache lookup, every dirty page goes through encryption before flush, every read decrypts. The exact cost depends on workload.
This obsoletes much of MDB_WRITEMAP. With chunk-based remapping, the "writable mmap, kernel handles writeback" path can't trivially carry encryption writemap-mode probably becomes incompatible with encryption. We lose the write performance optimisation.
Key management is deliberately not solved That's for LumoSQL to sort out, as is proper.