8000 feat(security): add per-namespace data keys and an encrypted file format by adeelahmad · Pull Request #77 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(security): add per-namespace data keys and an encrypted file format - #77

Merged
adeelahmad merged 4 commits into
mainfrom
claude/project-thread-a0ft5t
Oct 3, 2026
Merged

adeelahmad merged 4 commits into
mainfrom
claude/project-thread-a0ft5t

Conversation

@adeelahmad
Copy link
Copy Markdown
Owner

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 encrypt migration command and passkey vaults come next.

How: app/domain/keyring.py gives each namespace a random AES-256-GCM data key, stored only wrapped in data_key:⟨space⟩. The server wrapper 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 removing server turns the namespace into a vault that opens only through unlock(). 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 in docs/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

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change; ! in the title)

Checklist:

  • The title is a Conventional Commit.
  • My code follows the code style of this project (make hooks checks it on commit).
  • I added or updated tests.
  • My change requires documentation updates, and I have updated docs/ accordingly.
  • Users need more than the title to understand it, so I wrote it under ## Unreleased in CHANGELOG.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

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
@adeelahmad adeelahmad self-assigned this Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0