8000 feat(security): lock a namespace to your passkeys by adeelahmad · Pull Request #110 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(security): lock a namespace to your passkeys - #110

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

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

Conversation

@adeelahmad
Copy link
Copy Markdown
Owner

Description

Before: every namespace's data key was also wrapped by the server's key, so the server could always open its files.

After: an owner can turn a namespace into a vault from its page (Admin › Namespaces › Vault). Once it's a vault, only its passkeys open its files. Each unlock lasts encryption.vault_minutes (60 by default). While it's locked, files answer 423 and queued work waits. Owners can add and remove passkeys while it's open (never the last one), lock it now, or make it ordinary again. There is no recovery code. Losing every passkey that opens a vault loses its files.

How:

  • WebAuthn PRF with a per-namespace salt gives a 32-byte secret, and HKDF-SHA-256 turns it into a key-encryption key that wraps the data key. The secret travels beside the signed answer and is never stored or logged.
  • Sealing encrypts the namespace's plain files before the server's wrapper is removed. Unlocking catches up on any file that arrived plain. A locked vault's plain working copies are removed as soon as no job holds them.
  • jobs.claim skips locked vaults. An admin's "remove all passkeys" is refused (409) while those passkeys are the only way into a vault, unless lose_vaults=true is passed.
  • Web app: signWithPasskeyPrf / prfRequestOptions in lib/auth/webauthn.ts, and components/vaults/vault-section.tsx.

Motivation and Context

Adeel asked for everything to be encrypted, with passkeys as the key. His decisions: passkeys are the only unlock factor, and vaults apply only to the namespaces he chooses. Builds on #77, #89 and #73.

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 (ruff, prettier, eslint, tsc clean; the 3 mypy errors in app/api/deps.py are on main already).
  • I added or updated tests: tests/api/test_vaults.py (7), __tests__/vault-section.test.tsx, __tests__/webauthn.test.ts.
  • My change requires documentation updates, and I have updated docs/ accordingly (docs/encryption.md#vaults).
  • Users need more than the title to understand it, so I wrote it under ## Unreleased in CHANGELOG.md.

No CI runner is connected, so the full local suites were run:

  • Backend: 506 passed, 5 skipped. The one failure is the LibreOffice conversion test, which fails because LibreOffice can't convert in this environment.
  • Frontend: 653 passed.

Known limit: the server can't prove that the PRF secret sent at seal time is one the passkey will reproduce. A buggy authenticator could therefore seal a vault that never opens again. Adding a second passkey right away is the mitigation, and the panel prompts for it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01R1moYBoaoY4cZFbhBWqzEJ


Generated by Claude Code

claude added 4 commits October 3, 2026 11:43
…yet)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1moYBoaoY4cZFbhBWqzEJ
Vault panel on each namespace's page (seal, unlock, lock, add and remove
passkeys, make ordinary again), a PRF signing helper, a vault encrypting
its files even with encryption.files off, vault_minutes validation, docs
and changelog.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1moYBoaoY4cZFbhBWqzEJ
Encrypt a namespace's files before its server key goes, catch up on
unlock, remove a locked vault's working copies, guard admin passkey
removal, owners-only lock and passkey owners, 409 on a key race, and no
double encryption when a file is swapped mid-check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1moYBoaoY4cZFbhBWqzEJ
@adeelahmad
adeelahmad merged commit ff6d870 into main Oct 4, 2026
1 of 5 checks passed
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