Secrets¶
zeroth.platform.secrets ¶
Secret provider, resolution, and redaction primitives.
SecretProviderConfigError ¶
Bases: RuntimeError
Secret-backend configuration is incomplete or invalid.
EnvSecretProvider ¶
Resolve secret references by reading process or injected environment variables.
Tenant-scoped lookups follow the convention
ZEROTH_SECRET__{TENANT}__{NAME} and fall back to the bare name, so a
single-tenant deployment can key secrets off deployment.tenant_id while
dev setups keep using plain env vars.
resolve ¶
Resolve a concrete ref from the environment; the tenant-scoped key wins.
resolve_many ¶
Resolve several refs; refs that do not resolve are omitted from the result.
resolve_secret ¶
resolve_secret(
logical_name: str,
*,
tenant_id: str | None = None,
deployment_ref: str | None = None,
) -> str | None
Resolve a logical name; the tenant-scoped token wins over the bare env token.
resolve_async
async
¶
Async variant of :meth:resolve (pure dict read; no thread hop needed).
resolve_many_async
async
¶
Async variant of :meth:resolve_many.
resolve_secret_async
async
¶
resolve_secret_async(
logical_name: str,
*,
tenant_id: str | None = None,
deployment_ref: str | None = None,
) -> str | None
Async variant of :meth:resolve_secret.
SecretProvider ¶
Bases: Protocol
Interface for resolving secret references to concrete values.
The optional tenant_id keyword scopes resolution to a single tenant
(one value per deployment under the single-tenant model). It defaults to
None so existing ref-based callers (http client, execution units) keep
working unchanged.
resolve ¶
Resolve a single secret reference.
resolve_many ¶
Resolve multiple secret references at once.
resolve_secret ¶
resolve_secret(
logical_name: str,
*,
tenant_id: str | None = None,
deployment_ref: str | None = None,
) -> str | None
Resolve a logical secret name (e.g. 'llm.openai') to a value.
Unlike :meth:resolve, which takes a concrete reference, this maps a
stable logical key onto whatever concrete secret the backend holds for
the given tenant. Returns None when unresolved; the caller decides
whether to fail closed.
SecretResolutionError ¶
Bases: RuntimeError
A required secret could not be resolved and no fallback is permitted.
Raised on a fail-closed path (e.g. allow_env_fallback=False) so a
missing key surfaces loudly at call time instead of silently falling
through to a process-global environment variable. Carries only the logical
name — NEVER a concrete secret value.
SecretResolver ¶
Resolve environment-variable models into a concrete runtime environment.
resolve_environment_variables ¶
Resolve variable models into an env dict; a missing secret ref raises KeyError.
resolve_environment_variables_async
async
¶
Async variant for event-loop callers (execution-unit dispatch).
known_secrets ¶
Snapshot every secret value resolved so far, keyed by secret ref.
redactor ¶
Build a :class:SecretRedactor seeded with every resolved secret value.
SecretRedactor ¶
Replace known secret values with stable redaction markers.
Build from named mappings or a sequence of (reference, value) seeds.
String references are normalized to the conventional uppercase marker;
tenant-qualified tuple references always use the opaque SECRET marker.
Equal values are intentionally reduced to one deterministic marker.
redact ¶
Recursively redact strings, dicts, and lists that contain known secrets.
TenantScopedVaultDriver ¶
Bind a shared Vault provider to one tenant before exposing persistence.
resource_definition
property
¶
Return the immutable logical-resource contract.
operations
property
¶
Return immutable canonical operations bound to this tenant scope.
resolve
async
¶
Resolve one logical secret through the bound production provider.
resolve_many
async
¶
Resolve several logical secrets within the bound tenant.
VaultSecretProvider ¶
VaultSecretProvider(
*,
addr: str,
mount: str = "secret",
token: str | None = None,
role_id: str | None = None,
secret_id: str | None = None,
cache_ttl: float = 300.0,
transport: BaseTransport | None = None,
async_transport: AsyncBaseTransport | None = None,
timeout: float = 10.0,
)
Resolve secrets from a Vault KV v2 mount with an in-memory TTL cache.
Parameters¶
addr:
Vault base address, e.g. https://vault.internal:8200.
mount:
KV v2 mount point (default secret).
token:
A Vault token. Either this or an AppRole (role_id + secret_id) is
required — :func:build_secret_provider enforces that at startup.
role_id / secret_id:
AppRole credentials; exchanged for a token lazily on first use.
cache_ttl:
Seconds a resolved value stays cached before a re-fetch.
transport:
Optional httpx transport (tests inject an httpx.MockTransport).
resolve ¶
Resolve a concrete ref, treated as a logical name under the tenant path.
resolve_many ¶
Resolve several refs; refs that do not resolve are omitted from the result.
resolve_secret ¶
resolve_secret(
logical_name: str,
*,
tenant_id: str | None = None,
deployment_ref: str | None = None,
) -> str | None
Resolve a logical name via the TTL cache, doing one sync Vault GET on a miss.
resolve_async
async
¶
Async variant of :meth:resolve, served by the pooled client.
resolve_many_async
async
¶
Async variant of :meth:resolve_many; refs that do not resolve are omitted.
resolve_secret_async
async
¶
resolve_secret_async(
logical_name: str,
*,
tenant_id: str | None = None,
deployment_ref: str | None = None,
) -> str | None
Async :meth:resolve_secret: cache first, then one single-flight fetch per key.
warm
async
¶
Best-effort async prefetch of (tenant_id, logical_name) pairs.
Failures here are swallowed (logged, redacted) — resolution still works
lazily via :meth:resolve_secret. entries defaults to nothing,
because Vault KV does not cheaply enumerate arbitrary paths; callers
that know their key set can pass it to pre-populate the cache.
build_secret_provider ¶
Return the single secret provider selected by settings.backend.
env(dev default) — reads process environment variables, with theZEROTH_SECRET__{TENANT}__{NAME}tenant-scoping convention.vault— reads a HashiCorp Vault KV-v2 mount. Requiresvault_addrplus either avault_tokenor a full AppRole pair (vault_role_id+vault_secret_id); otherwise raises.
normalize_secret_name ¶
Map a logical secret name to its ENV-VAR token form.
'llm.openai' -> 'LLM_OPENAI'; 'signing.deployment' ->
'SIGNING_DEPLOYMENT'. Dots and dashes become underscores and the whole
string is upper-cased so it composes into the
ZEROTH_SECRET__{TENANT}__{NAME} convention.
resolve_async
async
¶
resolve_async(
provider: SecretProvider,
secret_ref: str,
*,
tenant_id: str | None = None,
) -> str | None
Resolve one secret ref without blocking the event loop.
Awaits the provider's native resolve_async when it has one; sync-only
providers (which may perform blocking I/O, e.g. Vault over HTTP) run via
asyncio.to_thread.
resolve_many_async
async
¶
resolve_many_async(
provider: SecretProvider,
refs: list[str],
*,
tenant_id: str | None = None,
) -> dict[str, str]
Resolve several secret refs without blocking the event loop.
resolve_secret_async
async
¶
resolve_secret_async(
provider: SecretProvider,
logical_name: str,
*,
tenant_id: str | None = None,
deployment_ref: str | None = None,
) -> str | None
Resolve a logical secret name without blocking the event loop.