Resolve Vault SecretRefs with the Bundled Vault Plugin
Learn how OpenClaw resolves exec SecretRefs from HashiCorp Vault using the bundled Vault plugin. This guide is for users who already operate Vault or prefer to keep credentials outside configuration files.
Read this when
- You want OpenClaw to read API keys from HashiCorp Vault
- You are setting up SecretRefs on a local machine or server
- You need to configure Vault-backed model provider credentials
Vault SecretRefs
Through the bundled Vault plugin, OpenClaw can resolve exec SecretRefs from HashiCorp Vault when the Gateway starts or reloads. These Vault references are kept in OpenClaw configuration, with resolved values held in the in-memory secrets snapshot. OpenClaw never writes the resolved API keys back to openclaw.json.
Choose this option if you already operate Vault or prefer to keep model provider credentials outside OpenClaw configuration files. For details on the SecretRef runtime model, refer to Secrets management.
Before you begin
These are required:
- OpenClaw with the bundled
vaultplugin available - a Vault server that is reachable
- Vault authentication capable of producing a client token with read access to the secret paths OpenClaw must resolve
- the environment launching the Gateway must contain
VAULT_ADDRand eitherVAULT_TOKEN,OPENCLAW_VAULT_AUTH_METHOD=token_filewithVAULT_TOKEN_FILE, or a configured JWT or Kubernetes login
The resolver communicates with Vault over HTTP from Node. No Vault CLI is needed on the Gateway for SecretRef resolution.
Enable the bundled plugin before executing the openclaw vault commands:
openclaw plugins enable vault
Store a provider key in Vault
By default, OpenClaw uses KV v2 mounted at secret, consistent with Vault dev-server examples. For a production Vault, set OPENCLAW_VAULT_KV_MOUNT to your actual KV mount path before creating SecretRef ids. With the OpenClaw defaults, this SecretRef id:
providers/openrouter/apiKey
reads this Vault field:
secret/data/providers/openrouter -> apiKey
One way to create it using the Vault CLI is:
export OPENROUTER_API_KEY=<openrouter-api-key>
vault kv put secret/providers/openrouter apiKey="$OPENROUTER_API_KEY"
Provide a scoped client token for OpenClaw rather than a root token. For the default KV v2 layout, a minimal policy for model provider keys looks like:
path "secret/data/providers/*" {
capabilities = ["read"]
}
Make Vault visible to the Gateway
For a local Gateway not running in a container, export Vault settings in the same shell that starts OpenClaw. The default auth method reads a Vault client token from VAULT_TOKEN:
export VAULT_ADDR=https://vault.example.com
export VAULT_TOKEN=<vault-client-token>
When Vault Agent writes a token sink file, use token-file auth instead:
export VAULT_ADDR=https://vault.example.com
export OPENCLAW_VAULT_AUTH_METHOD=token_file
export VAULT_TOKEN_FILE=/vault/secrets/token
If your Vault server uses a private CA, either install that CA in the host trust store and enable Node system trust:
export NODE_USE_SYSTEM_CA=1
Or supply a PEM bundle directly:
export NODE_EXTRA_CA_CERTS=/path/to/vault-ca.pem
These variables must be set when OpenClaw starts. The Vault plugin passes them to its resolver process.
For non-interactive JWT auth, provide a workload JWT file and a Vault role of type jwt:
export VAULT_ADDR=https://vault.example.com
export OPENCLAW_VAULT_AUTH_METHOD=jwt
export OPENCLAW_VAULT_AUTH_MOUNT=jwt
export OPENCLAW_VAULT_AUTH_ROLE=openclaw
export OPENCLAW_VAULT_JWT_FILE=/var/run/secrets/tokens/vault
The JWT file should be a projected workload token, such as a Kubernetes service account token with an audience the Vault role accepts. Interactive OIDC browser login works well for humans, but Gateway runtime requires non-interactive JWT login or a token file.
For Vault's Kubernetes auth method, use kubernetes. This is designed for Gateways running as Pods; the default mount is kubernetes, and the default JWT file is the standard service account token path:
export VAULT_ADDR=https://vault.example.com
export OPENCLAW_VAULT_AUTH_METHOD=kubernetes
export OPENCLAW_VAULT_AUTH_ROLE=openclaw
Set OPENCLAW_VAULT_AUTH_MOUNT only when Vault mounted Kubernetes auth somewhere other than auth/kubernetes. Set OPENCLAW_VAULT_JWT_FILE only when the service account token is projected at a custom path.
Optional settings:
export VAULT_NAMESPACE=<namespace-name>
export OPENCLAW_VAULT_KV_MOUNT=secret
export OPENCLAW_VAULT_KV_VERSION=2
Check what the current shell can see:
openclaw vault status
When more than one Vault-backed secret provider is configured, pick one by alias:
openclaw vault status --provider-alias corp-vault
openclaw vault status never prints VAULT_TOKEN; it only reports whether the token, token file, and JWT file are set.
Warning
If the Gateway runs as a service, LaunchAgent, systemd unit, scheduled task, or container, that runtime environment must receive the same Vault variables. Setting variables in an interactive shell only applies to that shell, not to the already-running Gateway.
Generate and apply a SecretRef plan
Create a plan that maps OpenRouter's model provider API key to Vault:
openclaw vault setup \
--plan-out ./vault-secrets-plan.json \
--openrouter-id providers/openrouter/apiKey
Apply and verify the plan:
openclaw secrets apply --from ./vault-secrets-plan.json --dry-run --allow-exec
openclaw secrets apply --from ./vault-secrets-plan.json --allow-exec
openclaw secrets audit --check --allow-exec
openclaw secrets reload
Use --allow-exec because the Vault plugin resolves through an OpenClaw-managed exec SecretRef provider.
If the Gateway is not yet running, start it normally after applying the plan instead of running openclaw secrets reload.
Configure more provider keys
Built-in shortcuts:
openclaw vault setup --openai-id providers/openai/apiKey
openclaw vault setup --anthropic-id providers/anthropic/apiKey
openclaw vault setup --openrouter-id providers/openrouter/apiKey
Multiple provider keys in one plan:
openclaw vault setup \
--plan-out ./vault-secrets-plan.json \
--openai-id providers/openai/apiKey \
--anthropic-id providers/anthropic/apiKey \
--openrouter-id providers/openrouter/apiKey
Bundled providers without shortcuts, or already-configured OpenAI-compatible and custom model providers, use --provider-key:
openclaw vault setup \
--plan-out ./vault-secrets-plan.json \
--provider-key local-openai=providers/local-openai/apiKey \
--provider-key groq=providers/groq/apiKey
Each --provider-key <provider=id> writes a SecretRef to models.providers.<provider>.apiKey. For custom providers, it does not create the provider's baseUrl, api, or models settings; configure those first.
Use --target <path=id> for any known SecretRef target path:
openclaw vault setup \
--target channels.telegram.botToken=channels/telegram/botToken \
--target models.providers.openai.headers.x-api-key=providers/openai/proxyKey \
--target auth-profiles:main:profiles.openai.key=providers/openai/apiKey
Bare target paths apply to openclaw.json. Use auth-profiles:<agentId>:<path> for existing auth-profiles.json targets. The target path must be a registered OpenClaw SecretRef target. The setup command does not create arbitrary named secrets in OpenClaw; Vault remains the secret store, and OpenClaw stores SecretRefs only on supported config fields.
SecretRef id format
Vault SecretRef ids follow this convention:
<vault-secret-path>/<field>
Examples:
| SecretRef id | Default KV v2 Vault read | Returned field |
|---|---|---|
providers/openrouter/apiKey | secret/data/providers/openrouter | apiKey |
providers/openai/apiKey | secret/data/providers/openai | apiKey |
teams/agent-prod/openrouter | secret/data/teams/agent-prod | openrouter |
The field returned by Vault must be a string.
For KV v1, configure it like this:
export OPENCLAW_VAULT_KV_VERSION=1
After that, providers/openrouter/apiKey reads the following:
secret/providers/openrouter -> apiKey
What OpenClaw stores
When you apply a Vault setup plan, a provider managed by the plugin gets stored:
{
"source": "exec",
"pluginIntegration": {
"pluginId": "vault",
"integrationId": "vault"
}
}
Credential fields then point to that provider:
{ "source": "exec", "provider": "vault", "id": "providers/openrouter/apiKey" }
The final value only exists in the active runtime secrets snapshot.
Containers and managed deployments
Containerized Gateways use the same plugin and SecretRef configuration. The container needs:
VAULT_ADDR- one authentication source:
VAULT_TOKENOPENCLAW_VAULT_AUTH_METHOD=token_filetogether withVAULT_TOKEN_FILEOPENCLAW_VAULT_AUTH_METHOD=jwtplusOPENCLAW_VAULT_AUTH_MOUNT,OPENCLAW_VAULT_AUTH_ROLE, andOPENCLAW_VAULT_JWT_FILEOPENCLAW_VAULT_AUTH_METHOD=kubernetesandOPENCLAW_VAULT_AUTH_ROLE; you can optionally overrideOPENCLAW_VAULT_AUTH_MOUNTorOPENCLAW_VAULT_JWT_FILE
- optionally
VAULT_NAMESPACE,OPENCLAW_VAULT_KV_MOUNT, andOPENCLAW_VAULT_KV_VERSION
On Kubernetes, prefer OPENCLAW_VAULT_AUTH_METHOD=kubernetes
when Vault has Kubernetes auth set up for the cluster. Use
OPENCLAW_VAULT_AUTH_METHOD=jwt only when Vault treats the cluster
as a generic JWT or OIDC issuer. Both options are preferable to a long-lived Vault
token stored in a Kubernetes Secret. Vault Agent sidecar or injector deployments can
use token_file instead.
For multi-tenant Vault setups, keep tenant routing in Vault policy and
deployment configuration. OpenClaw does not require a fixed mount, role, or path: each
Gateway environment can set its own OPENCLAW_VAULT_KV_MOUNT,
OPENCLAW_VAULT_AUTH_ROLE, and SecretRef ids. If a single shared Gateway must resolve
different Vault users simultaneously, use manually configured exec providers
that wrap distinct auth environments, or split tenants across Gateway
environments with separate Vault env.