"""Vault backend abstraction -- the ONLY layer that talks to the vault. bitwarden-cli official `bw` subprocesses against a Bitwarden-compatible server (transition backend). openbao OpenBao/Vault HTTP API (KV v2 + AppRole), stdlib urllib only -- the target backend for agent credentials. A failing backend surfaces as VAULT_BACKEND_ERROR; there is no fallback. The backend owns authentication, the unlocked session and vault reads. Everything else (policy, refs, adapters, sanitizer, MCP, unix socket) is backend-agnostic. """ from __future__ import annotations from ..errors import BrokerError class VaultBackend: """Interface consumed by BrokerCore. unlock(password) authenticate + establish session (raises UNLOCK_FAILED on bad password, VAULT_BACKEND_ERROR otherwise) lock() invalidate session + drop in-memory material list_metadata() items with secret fields stripped (ref index) get_item(item_id) full normalized item, fetched on demand """ name = "abstract" def __init__(self, config): self.config = config def configured(self) -> bool: raise NotImplementedError def unlock(self, password: str) -> None: raise NotImplementedError def lock(self) -> None: raise NotImplementedError def logout(self) -> None: """End the vault session AND the stored account login. Default equals lock() for backends without a persistent login.""" self.lock() def list_metadata(self) -> list[dict]: raise NotImplementedError def get_item(self, item_id: str) -> dict: raise NotImplementedError def make_backend(config) -> VaultBackend: name = config.vault_backend if name == "bitwarden-cli": from .bwcli import BitwardenCliBackend return BitwardenCliBackend(config) if name == "openbao": from .openbao import OpenBaoBackend return OpenBaoBackend(config) raise BrokerError( "UNCONFIGURED", f"unknown vault_backend {name!r} " "(supported: 'bitwarden-cli', 'openbao')", )