Repository navigation
feat(security): add per-namespace data keys and an encrypted file format - #77
Merged
Merged
Conversation
Each namespace gets a random AES-256-GCM data key, stored only wrapped by key-encryption keys (a server key derived with HKDF-SHA-256, and later passkey PRF keys). Files are encrypted in seekable chunks with truncation and reordering detection. Keys can rotate and a namespace can become a vault that opens only with its own keys. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R1moYBoaoY4cZFbhBWqzEJ
…anding Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R1moYBoaoY4cZFbhBWqzEJ
…les in one pass Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R1moYBoaoY4cZFbhBWqzEJ
This was referenced Oct 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Adeel · project thread
Description
Before: only secrets (source credentials, notification secrets, model API keys) were encrypted, all with one server key.
After: Lens has a keyring for encryption at rest. Nothing a user sees changes yet. This is the first of several PRs: files stored under the data folder, a
lens encryptmigration command and passkey vaults come next.How:
app/domain/keyring.pygives each namespace a random AES-256-GCM data key, stored only wrapped indata_key:⟨space⟩. Theserverwrapper is derived with HKDF-SHA-256 from the existing server secret, so background work keeps running. More wrappers can be added (passkey PRF keys come later), and removingserverturns the namespace into a vault that opens only throughunlock(). Keys rotate by version, and older data stays readable. Files use a chunked STREAM format (64 KiB chunks, the last chunk flagged in the nonce, the header bound as AAD), so ranges can be read without decrypting the whole file, and truncation, bit flips and reordered chunks are all rejected.plain_path()gives tools like ffmpeg a temporary plaintext copy. The design and its limits (the database and search indexes stay server-readable, and disk encryption is recommended) are indocs/encryption.md.Motivation and Context
Adeel asked for everything to be encrypted, with passkeys as the unlock factor. This builds the core every later step uses.
Types of changes
!in the title)Checklist:
make hookschecks it on commit).docs/accordingly.## UnreleasedinCHANGELOG.md.Full local backend suite: 382 passed, 4 skipped. The one failure is
test_conversion.py::test_office_and_text_files_become_documents, the known LibreOffice environment failure. Ruff, format and mypy are clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01R1moYBoaoY4cZFbhBWqzEJ
Generated by Claude Code