name: vault description: "Call HashiCorp Vault / OpenBao through Warden — read secrets, sign, encrypt, manage PKI — without ever holding a Vault token." category: provider-guide provider: vault requires: [foundation, discovery] upstream: HashiCorp Vault / OpenBao
Vault through Warden
What it does
Warden proxies Vault HTTP API requests. The agent sends a normal
Vault request to a Warden URL; Warden authenticates the agent
(JWT or cert), mints a short-lived Vault token from the configured
credential spec, injects it as X-Vault-Token, and forwards.
The agent never holds a Vault token.
Configure the CLI/SDK
<mount-url> and <role> below come from the discovery flow:
<mount-url>is the chosen provider'smount_urlfromwarden provider list(e.g./v1/vault/,/v1/team-data/secrets-prod/). Warden has already baked the namespace + mount path in.<role>is the role you picked fromwarden role listto perform this task — it goes in the URL path.
Auth is your JWT:
URL pattern : $WARDEN_ADDR<mount-url>role/<role>/gateway/<vault-api-path>
Auth header : Authorization: Bearer $WARDEN_TOKEN # OR X-Vault-Token: $WARDEN_TOKEN
Vault / OpenBao CLI
The Vault CLI works against Warden unchanged — point it at the
Warden gateway and use the JWT as the Vault token. Warden detects
the JWT prefix in X-Vault-Token and treats it as identity.
Use whichever binary the environment has. OpenBao ships its CLI
as bao (a fork of vault); some environments install one, some
the other, some both. The two are command-compatible — pick the one
on PATH. Probe order:
if command -v vault >/dev/null; then CLI=vault
elif command -v bao >/dev/null; then CLI=bao
else echo "neither vault nor bao is installed" >&2; exit 1
fi
Both honour VAULT_ADDR + VAULT_TOKEN (yes, bao reads
VAULT_* env vars too):
export VAULT_ADDR="$WARDEN_ADDR<mount-url>role/<role>/gateway"
export VAULT_TOKEN="$WARDEN_TOKEN"
$CLI kv get secret/myapp/config
$CLI kv put secret/myapp/config foo=bar
$CLI list secret/myapp/
$CLI write pki/sign/server-tpl csr=@./req.csr
The agent never holds a real Vault token; the JWT is the identity.
Vault SDK
For the official Go / Python / Node Vault SDKs, the same shape: set
the client's Address to $WARDEN_ADDR<mount-url>role/<role>/gateway
and its Token to the JWT.
Raw HTTP (curl)
curl -H "Authorization: Bearer $WARDEN_TOKEN" \
"$WARDEN_ADDR<mount-url>role/<role>/gateway/v1/secret/data/myapp/config"
Examples
(All examples assume mount_url = /v1/vault/ and role secrets-reader;
substitute yours. VAULT_ADDR and VAULT_TOKEN are exported as
shown above.)
KV v2 read:
vault kv get secret/app/db
KV v2 write (operator-permitted):
vault kv put secret/app/db key=value
Cross-namespace reads (Vault Enterprise / OpenBao namespaces — pass
the upstream namespace via VAULT_NAMESPACE, distinct from Warden's
namespace):
VAULT_NAMESPACE=prod vault kv get secret/app/db
PKI sign:
vault write pki/sign/server-tpl csr=@./req.csr
Same operations via raw HTTP for scripting:
curl -H "Authorization: Bearer $WARDEN_TOKEN" \
$WARDEN_ADDR/v1/vault/role/secrets-reader/gateway/v1/secret/data/app/db
Quirks
/v1is auto-prepended. A request to…/gateway/secret/data/foois rewritten to…/secret/data/fooupstream as if you'd written…/gateway/v1/secret/data/foo. Either form works.X-Vault-Namespaceheader passes through — useful for Vault Enterprise / OpenBao namespaces, distinct from Warden's namespaces.- PKI verify endpoints are unauthenticated at the Vault level; Warden still requires a valid identity to reach them.
- Returned data has Vault's normal envelope (
{data: {data: {…}}}for KV v2, etc.) — the agent decodes per Vault's API.