axm-vault
Catalog-resolver secrets manager (keyring + SecretStr) for AXM
Installation
Quick Start
Declare the credentials a package needs — the catalog describes schema only, it never holds a secret value:
Python
from axm_vault import CredentialGroup, CredentialSpec
group = CredentialGroup(
id="acme",
package="axm-acme",
title="Acme",
specs=(CredentialSpec(name="api_key", env="ACME_API_KEY", kind="token"),),
)
spec = group.spec("api_key") # -> CredentialSpec(name='api_key', ...)
Resolve a value by walking the layer precedence
(env > file > keyring > default > prompt) — the file tier is delegated to
axm-config, the keyring tier is consulted only for SECRET specs:
Python
from axm_vault import Resolver, get
resolved = Resolver().resolve(group, "api_key")
resolved.value, resolved.layer # e.g. ("s3cr3t", "env")
api_key = get("acme", "api_key") # singleton convenience -> just the value
Features
- ✅ Value-less catalog — models describe credential schema only, never store a secret
- ✅ Entry-point discovery —
load_catalog()aggregatesaxm.credentialsgroups (empty-safe, cached) - ✅ Layered resolution —
Resolverwalksenv > file > keyring > default > prompt; file tier delegated toaxm-config, keyring only forSECRET - ✅ Typed binding —
bind(model, group)builds a pydantic model from resolved values,SECRETfields asSecretStr - ✅ Value-free doctor —
doctor_data()/vault_doctorreport each credential's{layer, present}provenance without ever returning a secret - ✅ MCP tools —
vault_doctor(provenance) andvault_set(keyring/config) ship asaxm.tools(MCP + CLI + DAG node) - ✅ Operator CLI —
axm-vaultexposessetup/get/set/rotate/doctor/path; interactivesetupis TTY-guarded and idempotent,getmasks secrets unless--reveal - ✅ Frozen & strict — immutable pydantic v2 models that forbid unknown fields
- ✅ Modern Python — 3.12+ with strict typing
- ✅ Tested — Full coverage with pytest