Skip to content

config — top-level modules

config.loader

FACADE — the config loader now lives in core.config.loader (bp-067, finding-0103).

core owns its config-loading code (self-contained, stdlib-only, network-free). This module is a thin re-export so the ~147 non-core importers (from config.loader import get_config, Config, …) are untouched — outside → core is the allowed arrow. It DEFINES nothing but the token-capable get_secret (the machinery zone MAY reach the network Vault path config.secrets_backend, which core may not); the env path delegates to core.config, the single source of truth.

The public API is re-exported explicitly (not import *) so get_secret is unambiguously the token-capable form here while core.config's stays env-only — the trust-boundary split, visible to the type checker. tests that MONKEYPATCH loader internals (LEVERS_OVERLAY/_LOCAL/get_config) must patch core.config.loader — the real module whose load_config runs (finding-0104).

get_secret(name, token=None)

Token-capable secret access (the machinery form). With no token: the environment (delegates to core.config). With a token: a Vault ephemeral token minted for the calling agent's role — this branch reaches config.secrets_backend (network-capable, hvac), which is why it lives OUT here and not in core (Invariant 1). Secrets are never stored in config, read by a model, or logged (Invariant 10).

config.secrets_backend

Vault as a per-interaction runtime authorization layer (design-notes/vault-runtime-auth.md).

The object-capability model already scopes store handles at the code level (the dreamer gets a MirrorView, never a raw vector store). This module closes the matching gap at the credential level: an agent never holds a real secret, only an ephemeral token minted by the supervisor and scoped to a named policy (role). A token that doesn't cover a path is denied — the agent learns nothing beyond "denied" (VaultPermissionDenied).

hvac (the Vault HTTP client) is real-Vault-only and lazily imported inside VaultClient, so importing this module — or config.loader, which imports it lazily too — never requires hvac to be installed. The import-firewall (ops/import_lint.py) additionally blocks hvac from ever appearing under core/: agents receive tokens in context (Phase 5), they never call Vault directly. This module lives in config/, one level below get_secret(), exactly like the design note's import-discipline section specifies.

Phase scope (Steps 4–5 of the security & attestation track, NOT a numbered phase): the primitives below (FakeVault, VaultClient, build_secrets_backend, and MintedToken) and the scope-enforcement tests that exercise them. Step 5 added the accessor half of MintedToken — a mint's non-secret audit handle, which an attestation records in vault_token_accessor to tie an action to its authorization (the Vault↔attestation join; never the token — see MintedToken). Threading minted tokens into the dreamer/curator/vault-sync call sites is explicitly deferred to Phase 5 (agent factory + dispatcher) per the design note's own framing — this module makes that wiring possible without itself performing it.

VaultPermissionDenied

Bases: Exception

Raised when a token is unknown, or known but its role's policy doesn't cover the path. Deliberately uninformative beyond that — the caller (an agent) must not learn why it was denied, only that it was; the Vault audit log (or FakeVault.denials in tests) is where the detail belongs (vault-runtime-auth.md §6 — denials are an alignment signal, not noise).

MintedToken dataclass

What a mint returns: the secret token AND its accessor — Vault hands back both in one response (resp["auth"]["client_token"] / ["accessor"]). The two live in different keyspaces and do different jobs (the Step-5 Vault↔attestation join, attestation-layer.md §2):

token — the credential. The agent uses it (get_secret(name, token=...)) and nothing else may. NEVER logged, attested, or shown to a model (Invariant 10). • accessor — a non-secret audit handle. It can look up a token's metadata or revoke it, but cannot authenticate or read any secret. THIS is what an attestation records in vault_token_accessor, tying an action to its Vault authorization without ever exposing the credential.

The supervisor holds the whole MintedToken: it passes .token to the agent (in context, Phase 5) and records .accessor in the attestation it emits for that action.

token instance-attribute

accessor instance-attribute

SecretsBackend

Bases: Protocol

What both FakeVault (tests) and VaultClient (real Vault) implement — the supervisor and get_secret(..., token=...) depend on this shape, never on which one is wired.

mint_token(role, ttl)

read_secret(name, token)

FakeVault dataclass

An in-memory dev/test double — no real Vault, no network, no subprocess. policies maps a role name to the exact set of secret names its tokens may read (the dev-mode analogue of an HCL policy's path stanzas, see ops/vault/policies/); secrets is the backing key-value store. Every mint and every read/deny is recorded for assertions.

policies instance-attribute

secrets = field(default_factory=dict) class-attribute instance-attribute

minted = field(default_factory=list) class-attribute instance-attribute

denials = field(default_factory=list) class-attribute instance-attribute

mint_token(role, ttl)

read_secret(name, token)

role_for_accessor(accessor)

The dev-mode analogue of Vault's lookup-accessor: resolve an accessor to the role it was minted for, WITHOUT the token. This is what makes the Step-5 join verifiable — an attestation's vault_token_accessor can be confirmed to match its claimed agent_role. Returns None for an unknown accessor (or for a token passed here — wrong keyspace).

VaultClient

Real Vault, via hvac. Construction is side-effect-free — no connection is opened until mint_token/read_secret is actually called — mirroring OllamaClient/lancedb.connect elsewhere: safe to build in a wiring test without a live Vault dev-server running.

supervisor_token is the supervisor's own bootstrap credential (the bottom turtle for this layer — placed in Keychain/env via get_secret("vault-supervisor-token"), same pattern as the attestation signing keys). It is used only to mint child tokens; reads always go through a freshly-scoped client built from the caller's token, never the supervisor's.

hvac is imported per-method, not in __init__: edge/bridge/bridge.py holds the same line for boto3 ("imported LAZILY... so tests with a fake client never require boto3 installed") — this is that pattern applied here, so a wiring test can construct and inspect a VaultClient (addr, kv_mount) with no Vault dev-server and no hvac installed; only an actual mint_token/read_secret call needs the real package.

addr = addr instance-attribute

kv_mount = kv_mount instance-attribute

mint_token(role, ttl)

read_secret(name, token)

build_secrets_backend(config=None)

Wire a real VaultClient from [secrets]None when disabled, the normal state until the owner stands up a Vault dev-server (Step 6 runbook). Unlike attestation's fail-closed signing gate, a missing supervisor token here is not a silent-fallback risk (there is no insecure fallback path to slip into) — it simply surfaces as an hvac auth error on the first real mint_token call, not at construction.