Credential Management

What you'll accomplish: Store and manage integration/provider credentials securely — and understand a deliberate security property: the vault has no read-back.

The vault

Hoziron ships a built-in credential vault (hoziron-extensions::secrets::VaultStore) — a SQLite-backed store where every value is AES-256-GCM encrypted per-row (never a single whole-file blob), keyed by a master key generated at <HOZIRON_HOME>/keys/vault.key with 0600 permissions.

# Store a credential — prompts for the value, not echoed to the terminal
# or passed as a CLI argument (never lands in shell history)
hoziron-cli vault set CLAIMCENTER_API_KEY

# List stored keys — names and timestamps only, never values
hoziron-cli vault list

# Remove a credential
hoziron-cli vault remove CLAIMCENTER_API_KEY

hoziron-cli vault init is a no-op today — the vault (database + master key) is created lazily by the daemon on first write. There's nothing to pre-initialize.

No read-back, by design (Issue #475)

There is no GET /vault/{key} and no hoziron-cli vault get — this is a deliberate non-goal, not a missing feature. The vault is write/rotate/delete only from every surface:

// crates/surfaces/hoziron-api/src/routes.rs
// No single-key `GET /vault/{key}` — the vault is write/rotate/delete only
// from the API; there is no read-back of a stored value (see the issue's
// non-goals and acceptance criterion #8). `GET /vault` returns key names and
// timestamps only, never values.

GET /vault (list) requires the vault:read action; PUT/DELETE /vault/{key} (write/remove) require vault:manage. Both are Admin-only — the same bar as API key management, and stricter than most other read actions: even the auditor role (which can read almost everything else, including the audit trail) cannot list vault key names or their values. See RBAC.

ActionRouteRole required
List key names + timestampsGET /vaultadmin only
Store / overwrite a valuePUT /vault/{key}admin only
DeleteDELETE /vault/{key}admin only

Referencing vault/env credentials from config

Provider and registry entries reference a credential by name, never by value:

[[provider]]
id          = "big.licence"
driver      = "anthropic"
api_key_env = "ANTHROPIC_API_KEY"   # the name of an env var, resolved lazily at request time
[[catalog.registries]]
name           = "internal"
url            = "https://packages.internal.company.com"
auth_token_env = "INTERNAL_REGISTRY_TOKEN"

For registry tokens specifically, resolution checks the vault before the named env var (Issue #475's shared resolve_env_secret chokepoint) — so auth_token_env = "INTERNAL_REGISTRY_TOKEN" is satisfied by either a vault entry named INTERNAL_REGISTRY_TOKEN or a literal environment variable of that name, vault taking priority.

Environment variables

For container deployments, inject credentials via environment variables directly — this is the standard pattern for secrets managed by your container platform:

export ANTHROPIC_API_KEY="sk-ant-..."

How credentials are resolved

Key principles:

  • Credentials are resolved lazily at request time, not at daemon startup — adding or removing a value takes effect on the next request, no restart needed.
  • config.toml and package manifests store only the name of the credential reference — never the value.
  • Error messages name the env var / vault key, never the secret value.

Security practices

  • Never put raw secrets in config.toml, MANIFEST.toml, or adapter.json — always reference by name (api_key_env, auth_token_env, or a vault key).
  • The vault encrypts at rest (AES-256-GCM, per-row); plain environment variables rely on OS/container-level security instead.
  • Use an external secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) and inject as environment variables in containerized deployments — Hoziron's own vault is the right choice for bare-metal/persistent secrets where an external manager isn't already in play.
  • REST/SOAP adapter contracts declare an env allowlist (integration.env) for anything base_url may reference — a var used but not declared there is a hard validation error, closing an ambient-env-var exfiltration path. See APIs § REST/SOAP adapter packages.

Next steps


Related: