Updated May 23, 2026 for the plugin-managed SecretRef provider contract.
This verifies an external Vault plugin installed from git:github.com/sallyom/claw-vault using canonical OpenClaw plugin id vault. The repo/package name remains claw-vault; the OpenClaw plugin id and SecretRef integration id are both vault.
OpenClaw model credentials still use a normal SecretRef:
{
"models": {
"providers": {
"openrouter": {
"baseUrl": "https://openrouter.ai/api/v1",
"apiKey": {
"source": "exec",
"provider": "vault",
"id": "providers/openrouter/apiKey"
},
"models": [
{
"id": "google/gemini-3.5-flash",
"name": "google/gemini-3.5-flash"
}
]
}
}
}
}The provider config points at the owning plugin integration instead of copying command and args into OpenClaw config:
{
"secrets": {
"providers": {
"vault": {
"source": "exec",
"pluginIntegration": {
"pluginId": "vault",
"integrationId": "vault"
}
}
}
}
}At runtime OpenClaw loads the active plugin manifest, verifies the plugin is installed/enabled/trusted, and materializes the exec resolver from secretProviderIntegrations.vault.
Environment tested:
- OpenShift namespace:
openclaw-bob - Pod/container:
openclaw-8468444d5c-g62bp, containergateway - Vault address:
http://vault.vault.svc:8200 - Plugin install source:
git:github.com/sallyom/claw-vault - Installed plugin commit:
1aa40622d046c638d92eeb781415ccd8c1362595 - No Vault token value was printed.
Command:
openclaw vault -hOutput:
OpenClaw 2026.5.22 (unknown) — All your chats, one OpenClaw.
Usage: openclaw vault [options] [command]
Manage Vault SecretRefs
Options:
-h, --help Display help for command
Commands:
help Display help for command
setup Create a Vault SecretRef setup plan
status Show Vault SecretRef provider status
Command:
openclaw vault status --jsonOutput:
{
"providerAlias": "vault",
"provider": {
"configured": true,
"source": "exec",
"pluginIntegration": {
"pluginId": "vault",
"integrationId": "vault"
}
},
"resolverScript": "/home/node/.openclaw/git/git-c317c86c2672b3ca/repo/dist/vault-secret-ref-resolver.js",
"vaultAddr": "http://vault.vault.svc:8200",
"kvMount": "secret",
"kvVersion": "2",
"hasVaultToken": true
}Sanitized installed manifest/runtime proof:
{
"manifest": {
"id": "vault",
"activation": {
"onStartup": false,
"onCommands": ["vault"]
},
"commandAliases": [
{
"name": "vault",
"kind": "cli"
}
],
"secretProviderIntegrations": {
"vault": {
"providerAlias": "vault",
"displayName": "HashiCorp Vault",
"source": "exec",
"command": "${node}",
"args": ["./vault-secret-ref-resolver.js"],
"timeoutMs": 5000,
"noOutputTimeoutMs": 5000,
"maxOutputBytes": 1048576,
"allowInsecurePath": true,
"passEnv": [
"VAULT_ADDR",
"VAULT_TOKEN",
"VAULT_NAMESPACE",
"CLAW_VAULT_KV_MOUNT",
"CLAW_VAULT_KV_VERSION",
"CLAW_VAULT_VALUES_JSON"
]
}
}
},
"distIndexHead": "definePluginEntry({ id: \"vault\", ... registerCli(...) })"
}{
"plugins": {
"allow": ["vault"],
"entries": {
"vault": {
"enabled": true
}
}
},
"secrets": {
"providers": {
"vault": {
"source": "exec",
"pluginIntegration": {
"pluginId": "vault",
"integrationId": "vault"
}
}
}
},
"models": {
"providers": {
"openrouter": {
"baseUrl": "https://openrouter.ai/api/v1",
"apiKey": {
"source": "exec",
"provider": "vault",
"id": "providers/openrouter/apiKey"
},
"models": [
{
"id": "google/gemini-3.5-flash",
"name": "google/gemini-3.5-flash"
}
]
}
}
}
}Command:
openclaw secrets audit --allow-exec --jsonRelevant output:
{
"version": 1,
"status": "findings",
"resolution": {
"refsChecked": 2,
"skippedExecRefs": 0,
"resolvabilityComplete": true
},
"summary": {
"plaintextCount": 1,
"unresolvedRefCount": 0,
"shadowedRefCount": 2,
"legacyResidueCount": 0
}
}unresolvedRefCount: 0 proves the configured exec SecretRefs resolved through the Vault provider. The findings in this deployment were non-blocking audit warnings: a plaintext gateway auth token and provider precedence shadowing.
openclaw models status --json showed openrouter/google/gemini-3.5-flash as allowed and provider auth profiles for openai and openrouter as SecretRef-backed (ref(exec:providers/.../apiKey)). A live model call was also confirmed separately in this deployment.
Note: in this specific pod, openclaw secrets reload --json from the CLI context was blocked by a Gateway scope-upgrade approval. That is an auth/scope issue for the command path, not a Vault resolver failure; secrets audit --allow-exec --json did execute the resolver and reported zero unresolved refs.
The plugin helper can generate the plan that writes the provider config and model SecretRefs:
openclaw vault setup \
--openrouter-id providers/openrouter/apiKey \
--plan-out ./vault-secrets-plan.json
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 --allow-exec --jsonopenclaw vault setup maps existing Vault secret ids into OpenClaw SecretRefs and provider config. It does not write secret values into Vault.
Older smoke tests copied this into OpenClaw config:
{
"source": "exec",
"command": "/usr/local/bin/node",
"args": ["/home/node/.openclaw/plugins/claw-vault/vault-secret-ref-resolver.js"],
"passEnv": ["VAULT_ADDR", "VAULT_TOKEN"]
}That is still possible as manual exec config, but it is no longer the plugin-managed contract. The plugin-managed contract stores plugin ownership (pluginId + integrationId) and lets OpenClaw materialize command details from the installed plugin manifest.