The browser vault now draws from three login sources behind one handle
shape: the local encrypted vault (vault_…), 1Password Login items (op:…)
and Bitwarden Password Manager logins (bw:…). browser_vault_list aggregates
metadata across them; browser_vault_fill routes by prefix and resolves the
password at fill time only, through the manager CLI.
External managers are locked until the user unlocks them for the current
session. The new browser_vault_unlock tool (and the fill path, implicitly)
asks the surface to show a masked master-password prompt — CLI panel
(reuses the sudo panel state), TUI/Desktop via a vault.unlock.request
blocking card. The password goes to `op signin --raw` / `bw unlock --raw`
on stdin, never argv or env; only the session token is kept, in memory,
with a 30-minute idle TTL, cleared on session close or `vault.lock`.
Headless contexts (cron, webhook, api_server, -q) can never prompt: the
manager is reported as locked with unlock=unavailable_in_this_session and
fill refuses — the same posture approvals take where nobody can answer.
Config: vault.onepassword / vault.bitwarden {enabled, binary_path, …};
a 1Password service-account token skips the prompt for headless use.
RPC: vault.sources, vault.source.set, vault.unlock, vault.lock for Settings.
Tests (2, real subprocess against a fake bw; each proven red by sabotage):
headless never prompts or spawns; unlock feeds stdin only, token never
enters os.environ, fill routes by prefix and the password only reaches the
fill script.
19 lines
961 B
Python
19 lines
961 B
Python
"""Login backends for the browser credential vault.
|
|
|
|
The local Fernet store (``agent/vault_store.py``) is one backend; 1Password
|
|
(``op``) and Bitwarden Password Manager (``bw``) are the others. Every backend
|
|
hands the agent the same shape — opaque handle + login metadata — and resolves
|
|
the password server-side at fill time only. Handles are namespaced by backend
|
|
(``vault_…`` local, ``op:…``, ``bw:…``) so the browser tools need no schema
|
|
change to route to the right one.
|
|
|
|
External managers are locked until the user unlocks them for the current
|
|
session (``agent/vault_backends/unlock.py``); the master password is typed
|
|
into a masked prompt owned by the surface (CLI panel, Desktop dialog) and is
|
|
never a tool argument, never argv, never persisted.
|
|
"""
|
|
|
|
from agent.vault_backends.base import LoginBackend, UnlockRequired, backend_for_handle, enabled_backends
|
|
|
|
__all__ = ["LoginBackend", "UnlockRequired", "backend_for_handle", "enabled_backends"]
|