feat(secrets): pluggable SecretSource interface + multi-source orchestrator

Introduces a first-class secret-source contract so password managers
(Bitwarden today, 1Password next, third-party vaults as plugins) plug
into one orchestrated startup path instead of each hardcoding into
env_loader.

- agent/secret_sources/base.py: SecretSource ABC (fetch-only contract:
  never raises, never prompts, sync with orchestrator-enforced timeout),
  shared ErrorKind taxonomy, FetchResult, run_secret_cli() minimal-env
  subprocess helper, API versioning for plugin compatibility.
- agent/secret_sources/registry.py: registration gating (name/scheme
  uniqueness, api_version, shape), apply_all() orchestrator owning
  precedence (mapped-beats-bulk, first-claim-wins, override_existing
  never crosses sources, protected bootstrap tokens), conflict warnings,
  per-var provenance, per-source wall-clock timeout.
- Bitwarden converted to a registered BitwardenSource (bulk shape);
  behavior unchanged, apply_bitwarden_secrets kept as legacy shim.
- env_loader._apply_external_secret_sources now drives the orchestrator;
  provenance labels resolve through registry (e.g. '(from 1Password)').
- PluginContext.register_secret_source() for external backends.
- secrets.sources optional ordering key in DEFAULT_CONFIG + example.
- tests/secret_sources/: 47 new tests incl. reusable conformance kit
  (SecretSourceConformance) that plugin authors run against their source.
This commit is contained in:
teknium1 2026-07-06 01:41:46 -07:00 committed by Teknium
parent d345b9fbfe
commit 2d16ec7fb7
14 changed files with 1616 additions and 110 deletions

View file

@ -1380,3 +1380,30 @@ updates:
# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET
# # in ~/.hermes/.env over putting it here in config.yaml.
# # client_secret: ""
# =============================================================================
# External secret sources
# =============================================================================
# Pull provider credentials from external secret managers at process startup
# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap
# token (e.g. BWS_ACCESS_TOKEN) lives in .env; everything else rotates
# centrally in the vault. Multiple sources can be enabled at once:
# - "mapped" sources (explicit VAR -> ref bindings) beat "bulk" sources
# (whole-project dumps like Bitwarden BSM)
# - within a shape, the first source to claim a var wins; later claims
# are skipped with a startup warning (never a silent clobber)
# - a source's override_existing lets it beat .env/shell values, but
# never another secret source's claim
# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/
#
# secrets:
# # Optional explicit ordering of enabled sources.
# # sources: [bitwarden]
# bitwarden:
# enabled: false
# project_id: "" # BSM project UUID
# access_token_env: BWS_ACCESS_TOKEN
# cache_ttl_seconds: 300 # 0 disables memory+disk caching
# override_existing: true # BSM wins over .env so rotation works
# auto_install: true # auto-download the pinned bws binary
# server_url: "" # e.g. https://vault.bitwarden.eu (EU cloud)