8000 feat(secrets): read secret settings from Vaultwarden by reference by adeelahmad · Pull Request #550 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(secrets): read secret settings from Vaultwarden by reference - #550

Merged
adeelahmad merged 3 commits into
mainfrom
claude/project-thread-56g82s
Oct 18, 2026
Merged

adeelahmad merged 3 commits into
mainfrom
claude/project-thread-56g82s

Conversation

@adeelahmad
Copy link
Copy Markdown
Owner

Requested by Adeel · project thread

Description

Before: every secret in Settings (model API keys, chat-app tokens, mail passwords) had to be typed into Lens, which kept it sealed with the server's key.

After: a secret field can hold a reference to a Vaultwarden or Bitwarden item instead, like vaultwarden://OpenAI/password. Lens reads the value from the vault and keeps it in memory for secrets.cache_minutes (10 by default). It then reads it again in the background, so a key rotated in the vault reaches Lens by itself. Only the reference is stored, and it's checked against the vault before it's saved. Settings, the API, MCP and the assistant show references and whether they could be read, never a value. Secrets saved in Lens stay the default.

How:

  • secret_providers.py is a small provider layer over the existing sealed settings: builtin, plus vaultwarden. A new password manager is one more Provider.
  • settings.effective() resolves references after the settings are built. Settings.current() rebuilds when a value read from the vault changes.
  • Vaultwarden is read with python-vaultwarden (Apache-2.0). It signs in with an API key, uses the master password for the vault's end-to-end encryption, and reads only one folder of Lens's own account (secrets.vaultwarden_folder, "Lens").
  • The client is a component fetched once a Vaultwarden address is set, so the default install carries nothing for it.

New for agents and the API:

  • GET /api/v1/secrets, GET /api/v1/secrets/items and POST /api/v1/secrets/test, for admins.
  • The MCP tools list_secrets, list_vault_items and set_secret_reference.
  • The assistant's tools list_secrets and vault_items. change_settings on the secrets section always asks first, and approval cards show references instead of "(a secret)".

Motivation and Context

Adeel asked for a secrets manager, starting with Vaultwarden. Others will follow.

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 check and format, and mypy, are clean.
  • I added or updated tests. tests/api/test_secrets.py runs against tests/fake_vaultwarden.py, a loopback Vaultwarden that encrypts with the library's own Bitwarden crypto. It covers logins, notes, cards, items with keys of their own, other folders and a refused sign-in. test_mcp.py lists the new tool. The full backend suite passes locally: 1644 passed, 23 skipped.
  • My change requires documentation updates, and I have updated docs/ accordingly: a new docs/secrets.md, plus the configuration, MCP, assistant and components pages.
  • Users need more than the title to understand it, so I wrote it under ## Unreleased in CHANGELOG.md.

Done questions (AGENTS.md)

  • Goal: this is the backend half, merged on green. Settings › Secrets in the web app follows in its own PR.
  • Agents: usable through the API, the MCP tools and the assistant's tools. Agents refer to secrets and check them, but never read them.
  • Zero setup: nothing changes until someone sets a Vaultwarden address. The client is then fetched as a component, with no .env edit.
  • Fewest clicks: one reference string per field. The vault's item list gives the reference to each field, ready to paste. The web picker is planned.
  • Configurable: the secrets.* settings have defaults and can be changed through PUT /settings/secrets and the assistant. The web section is next. The setup wizard is unchanged.
  • Light: python-vaultwarden is an optional extra, fetched only once an address is set.
  • Private and secure: the vault's API key and master password are sealed like any secret. Values are held in memory only. Lens reads one folder of its own account. The threat model adds P-SECRET-REFERENCES, a §1.7 entry point and a §1.9 dependency.
  • Cost: n/a, there are no model calls. Vault reads are cached for secrets.cache_minutes.
  • User decides: built-in secrets stay the default. Assistant changes to the Secrets section always wait for approval.
  • Upgrades: n/a, the shape of stored data is unchanged. A reference is stored where the sealed secret was, and existing sealed values read as before.
  • Honest: the docs and README label the web UI as planned and claim only what is merged.
  • Tests, docs and generated files updated: openapi.json and the client were regenerated with the make openapi steps, and uv.lock with uv lock.
  • Open: dependencies keep Lens MIT. python-vaultwarden is Apache-2.0, and its new dependencies, argon2-cffi-bindings and hkdf, are MIT and BSD. The Bitwarden CLI (GPL) and Bitwarden's SDK (not permissively licensed) were ruled out.
  • Reported: the "refine later" items are written down in docs/secrets.md and DEVLOG.md:
    • references in sources, MCP servers and sign-in providers
    • checking values read from the vault
    • a custom CA for a self-hosted vault
    • an environment variable for the master password
    • more password managers
  • Reuse: builds on the sealed settings, components and pip_install, and uses the library's sign-in and crypto rather than new crypto.

Generated by Claude Code

A secret field in Settings can hold a reference such as
vaultwarden://OpenAI/password instead of the secret. Lens reads the value
from the vault when it builds its settings, keeps it in memory for
secrets.cache_minutes and reads it again in the background; only the
reference is stored, and it's checked against the vault before it's saved.
Settings, the API, MCP and the assistant show references and whether they
could be read, never a value. The built-in sealed store stays the default.

Vaultwarden is read with python-vaultwarden (Apache-2.0), fetched as a
component once an address is set; Lens reads one folder of its own account.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019okSDRfFHV74BTpMzinTFg
@adeelahmad adeelahmad self-assigned this Oct 18, 2026
…-56g82s

# Conflicts:
#	CHANGELOG.md
#	docs/mcp.md
…-56g82s

# Conflicts:
#	fastapi_backend/app/api/mcp_tools.py
#	fastapi_backend/app/domain/ops_tools.py
#	fastapi_backend/tests/api/test_mcp.py
@adeelahmad
adeelahmad merged commit 855ff1c into main Oct 18, 2026
20 checks passed
@adeelahmad
adeelahmad deleted the claude/project-thread-56g82s branch October 18, 2026 07:47
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