Files
me be87900794 agent-credential-broker: fork of vaultwarden-agent-broker + OpenBao backend
Mechanical rename (package agent_credential_broker; entry points
agent-broker{,-mcp,-plugin}; env AGENT_BROKER_*; paths agent-broker;
agent-browser credential provider 'broker'; MCP server name agent-broker).

New backend 'openbao': stdlib-only HTTP client for OpenBao/Vault KV v2
(approle login or token file/env; metadata-only ref index; lazy re-login
on 401/403; token renewal). bitwarden-cli backend kept as transition.
2026-10-14 13:27:33 -06:00

4.7 KiB

agent-credential-broker

Capability layer between AI agents and a credential vault: agents use credentials, they can never read them.

Forked from the Vaultwarden-era vaultwarden-agent-broker (same socket protocol, MCP surface, policy model and adapters; renamed package, binaries, env prefix and paths). The design rationale for the backend split lives in backlog/02_password_manager/broker-design.md.

Backends

Backend Status Auth Notes
openbao target backend AppRole (role_id/secret_id files, 0600) or direct token (0600 file / AGENT_BROKER_OPENBAO_TOKEN) KV v2 at <mount>/data/<prefix>/<ref>; the ref index is built only from metadata/*, so unlock never transits plaintext
bitwarden-cli transition bw subprocess, API key + master password unchanged; kept for the migration window, will be dropped with the old vault

Select with vault_backend in the config JSON.

Item contract (OpenBao)

secret/data/agent/<ref>       {username, password, totp, uris, notes, ...}
secret/metadata/agent/<ref>   custom_metadata:
                                agent.ref, agent.adapters, agent.allowed_urls,
                                agent.allowed_hosts, agent.auth_type,
                                agent.confirmation, agent.*_selector,
                                agent.ssh_user, agent.ssh_key_path,
                                agent.cli_profile, agent.insecure_http
                                label (display name), uris (csv or JSON list)

agent.ref is the stable, unique, exact-match reference. Every agent.* field and its effect is documented in agent_credential_broker/policy.py. Paths may nest (agent/<sub>/<ref>) — the item id is the path relative to the prefix.

Minimal AppRole policy:

path "secret/data/agent/*"     { capabilities = ["read"] }
path "secret/metadata/agent/*" { capabilities = ["read", "list"] }

Quick start

pip3 install --user -e .

# 1. credentials dir + AppRole files (or openbao-token.txt)
install -d -m 700 ~/.config/devin/credentials
# openbao-role-id.txt / openbao-secret-id.txt   (600)

# 2. config -- ~/.config/agent-broker/config.json (or AGENT_BROKER_CONFIG)
#    {
#      "vault_backend": "openbao",
#      "openbao_addr": "http://127.0.0.1:18200",
#      "openbao_mount": "secret",
#      "openbao_prefix": "agent",
#      "openbao_role_id_file": "~/.config/devin/credentials/openbao-role-id.txt",
#      "openbao_secret_id_file": "~/.config/devin/credentials/openbao-secret-id.txt",
#      "openbao_ca_cert": "/path/ca.pem",          # optional, https only
#      "openbao_tls_skip_verify": false,            # dev only
#      "openbao_namespace": null,                    # -> X-Vault-Namespace
#      "unlock_ttl_seconds": 3600
#    }

# 3. daemon (unix socket only)
agent-broker daemon &           # or: make service-install

# 4. unlock -- openbao ignores the password value (non-interactive by
#    design, the 0600 files are the credential); enter anything or set
#    master_password_file for unattended unlock
agent-broker unlock

# 5. refs only
agent-broker list-refs
agent-broker status
agent-broker lock

Interfaces

Consumer Entry point
Agent (MCP) agent-broker-mcp (stdio, server name agent-broker)
agent-browser --credential-provider broker -> agent-broker-plugin
Local human agent-broker CLI (daemon / status / unlock / lock / confirm)

Threat model (unchanged)

  • Unix socket only ($XDG_RUNTIME_DIR/agent-broker/broker.sock, 0600, dir 0700), same-UID enforced via SO_PEERCRED. No TCP listener.
  • Decrypted items exist only in daemon RAM, wiped after each use (wipe_item), auto-locked after unlock_ttl_seconds.
  • The ref index holds metadata only (id/type/name/uris/agent.* fields). On OpenBao it is built from metadata/* endpoints — no plaintext transit at all.
  • The agent-facing surface (MCP tools, socket methods) is a static allowlist; a startup assert forbids any tool named like a secret reader (get_*, reveal, dump, ...).
  • Adapters inject secrets via child env / askpass-on-tmpfs / the agent-browser plugin — never argv — and every byte that crosses the boundary is sanitized (sanitize.py).
  • Same-UID local malware remains out of scope: it can read the 0600 credential files directly. The boundary is the agent, not the host.

Tests

make test     # or: python -m pytest tests/ -x -q
make lint

All tests are offline fixtures (tests/fakebw.py, tests/fake_bao.py) — no real vault, no real credentials.