HashiCorp Vault Plugin
Our HashiCorp Vault plugin enables secure loading of secrets from HashiCorp Vault (KV v2 secrets engine) and OpenBao using declarative instructions within your .env files.
The plugin supports multiple authentication methods including explicit tokens, AppRole for CI/CD, and automatic CLI token detection for local development.
Features
Section titled “Features”- Zero-config authentication - Automatically uses Vault token from CLI login
- AppRole authentication - For automated and CI/CD workflows
- Vault CLI integration - Works seamlessly with
vault loginfor local development - OpenBao compatible - Works with OpenBao (detects
~/.bao-tokenautomatically) - Auto-infer secret keys from environment variable names
- JSON key extraction from secrets using
#syntax or namedkeyparameter - Path prefixing with
pathPrefixoption for organized secret management - Default path support for sharing a common secret path across items
- Support for Vault Enterprise namespaces
- Support for multiple Vault instances
Installation and setup
Section titled “Installation and setup”In a JS/TS project, you may install the @varlock/hashicorp-vault-plugin package as a normal dependency.
Otherwise you can just load it directly from your .env.schema file, as long as you add a version specifier.
See the plugins guide for more instructions on installing plugins.
# 1. Load the plugin# @plugin(@varlock/hashicorp-vault-plugin)## 2. Initialize the plugin - see below for more details on options# @initHcpVault(url="https://vault.example.com:8200")Authentication options
Section titled “Authentication options”The plugin tries authentication methods in this priority order:
- Explicit token - If
tokenis provided in@initHcpVault() - AppRole - If both
roleIdandsecretIdare provided - CLI token file - From
~/.vault-token(created byvault login) or~/.bao-token(created bybao loginfor OpenBao)
Automatic authentication (Recommended for local dev)
Section titled “Automatic authentication (Recommended for local dev)”For local development, just provide the Vault URL - the plugin will pick up your CLI token automatically:
# @plugin(@varlock/hashicorp-vault-plugin)# @initHcpVault(url="https://vault.example.com:8200")How this works:
- Local development: Run
vault login→ automatically uses the token from~/.vault-token - OpenBao users: Run
bao login→ automatically uses the token from~/.bao-token
AppRole auth (For CI/CD and automated workflows)
Section titled “AppRole auth (For CI/CD and automated workflows)”AppRole is the recommended auth method for CI/CD and server environments:
-
Set up AppRole in Vault (see Vault Setup section below)
-
Wire up the credentials in your config. Add config items for the role ID and secret ID, and reference them when initializing the plugin.
.env.schema # @plugin(@varlock/hashicorp-vault-plugin)# @initHcpVault(# url="https://vault.example.com:8200",# roleId=$VAULT_ROLE_ID,# secretId=$VAULT_SECRET_ID# )# ---VAULT_ROLE_ID=# @sensitiveVAULT_SECRET_ID= -
Set your credentials in deployed environments. Use your platform’s env var management UI to securely inject these values.
Explicit token
Section titled “Explicit token”You can also provide a token directly:
# @initHcpVault(# url="https://vault.example.com:8200",# token=$VAULT_TOKEN# )# ---
# @type=vaultToken @sensitiveVAULT_TOKEN=Vault Enterprise namespaces
Section titled “Vault Enterprise namespaces”For Vault Enterprise, specify the namespace:
# @initHcpVault(url="https://vault.example.com:8200", namespace="admin/team-a")Multiple instances
Section titled “Multiple instances”If you need to connect to multiple Vault instances, register named instances:
# @initHcpVault(id=prod, url="https://vault-prod.example.com:8200")# @initHcpVault(id=dev, url="https://vault-dev.example.com:8200")# ---
PROD_KEY=vaultSecret(prod, "secret/api/keys#API_KEY")DEV_KEY=vaultSecret(dev, "secret/api/keys#API_KEY")Loading secrets
Section titled “Loading secrets”Once the plugin is installed and initialized, you can start adding config items that load values using the vaultSecret() resolver function.
Basic usage
Section titled “Basic usage”Since Vault KV v2 always stores key/value pairs, the item key (variable name) is automatically used as the JSON key to extract from the secret:
# Fetches "secret/db/config" and extracts "DB_HOST" keyDB_HOST=vaultSecret("secret/db/config")
# Override the extracted key with # syntaxDB_PASSWORD=vaultSecret("secret/db/config#password")
# Or use named "key" parameterDB_PORT=vaultSecret("secret/db/config", key="PORT")
# Fetch entire secret as JSON blobDB_CONFIG=vaultSecret("secret/db/config", raw=true)Default path
Section titled “Default path”Use defaultPath to set a common path for secrets when no path argument is provided:
# @initHcpVault(url="https://vault.example.com:8200", defaultPath=secret/myapp/config)# ---
# Both fetch from "secret/myapp/config" extracting item keyDB_PASSWORD=vaultSecret()API_KEY=vaultSecret()
# Override the inferred key using # syntaxSTRIPE_KEY=vaultSecret("#stripe_api_key")
# Explicit path still extracts item key by defaultOTHER_SECRET=vaultSecret("secret/other/path")Path prefixing
Section titled “Path prefixing”Use pathPrefix to automatically prefix all secret paths for better organization:
# @initHcpVault(url="https://vault.example.com:8200", pathPrefix="secret/myapp")# ---
# Fetches from "secret/myapp/db/config"DB_HOST=vaultSecret("db/config#HOST")You can even use dynamic prefixes:
# @initHcpVault(url="https://vault.example.com:8200", pathPrefix="secret/${ENV}")# ---
# In prod: fetches from "secret/prod/db/config"# In dev: fetches from "secret/dev/db/config"DB_HOST=vaultSecret("db/config#HOST")Bulk loading secrets
Section titled “Bulk loading secrets”Use raw=true with @setValuesBulk to load all key/value pairs from a Vault path at once, instead of wiring up each secret individually:
# @initHcpVault(url="https://vault.example.com:8200")# @setValuesBulk(vaultSecret("secret/myapp/config", raw=true))# ---
DB_HOST=DB_PASSWORD=API_KEY=This fetches all keys stored at secret/myapp/config and maps them to matching item keys. Only items declared in your schema will be populated — any extra keys in the Vault secret are ignored.
Vault Setup
Section titled “Vault Setup”Enable KV v2 secrets engine
Section titled “Enable KV v2 secrets engine”# KV v2 is enabled by default at "secret/" in dev mode# For production, enable it explicitly:vault secrets enable -version=2 -path=secret kvCreate a policy
Section titled “Create a policy”Create a policy that allows reading secrets:
path "secret/data/*" { capabilities = ["read"]}vault policy write varlock-reader policy.hclSet up AppRole auth (Recommended for CI/CD)
Section titled “Set up AppRole auth (Recommended for CI/CD)”-
Enable AppRole auth method
Terminal window vault auth enable approle -
Create a role
Terminal window vault write auth/approle/role/varlock-role \secret_id_ttl=24h \token_ttl=1h \token_max_ttl=4h \token_policies=varlock-reader -
Get the role ID and generate a secret ID
Terminal window vault read auth/approle/role/varlock-role/role-idvault write -f auth/approle/role/varlock-role/secret-idSave the
role_idandsecret_idfrom the output for your CI/CD configuration.
Create a token (For simple setups)
Section titled “Create a token (For simple setups)”vault token create -policy=varlock-reader -ttl=24hStore secrets
Section titled “Store secrets”# Store a single key/valuevault kv put secret/myapp/config DB_PASSWORD=supersecret
# Store multiple keysvault kv put secret/myapp/config \ DB_HOST=db.example.com \ DB_PASSWORD=supersecret \ API_KEY=abc123Vault CLI for local development
Section titled “Vault CLI for local development”-
Set the Vault address
Terminal window export VAULT_ADDR="https://vault.example.com:8200" -
Login to Vault
Terminal window vault loginThis writes a token to
~/.vault-tokenwhich the plugin will automatically pick up. -
Test the configuration
Terminal window vault kv get secret/myapp/config
Reference
Section titled “Reference”Root decorators
Section titled “Root decorators”@initHcpVault()
Section titled “@initHcpVault()”Initialize a HashiCorp Vault / OpenBao plugin instance.
Key/value args:
url(required): Vault server URL (e.g.,https://vault.example.com:8200)token(optional): Explicit Vault authentication tokenroleId(optional): AppRole role ID for automated authenticationsecretId(optional): AppRole secret ID for automated authenticationnamespace(optional): Vault Enterprise namespacedefaultPath(optional): Default secret path when no path argument is given tovaultSecret()pathPrefix(optional): Prefix automatically prepended to all secret pathsid(optional): Instance identifier for multiple instances
# @initHcpVault(url="https://vault.example.com:8200", defaultPath=secret/myapp/config)Data types
Section titled “Data types”vaultToken
Section titled “vaultToken”Represents a HashiCorp Vault authentication token. This type is marked as @sensitive.
# @type=vaultTokenVAULT_TOKEN=Resolver functions
Section titled “Resolver functions”vaultSecret()
Section titled “vaultSecret()”Fetch a secret from HashiCorp Vault’s KV v2 secrets engine.
Array args:
instanceId(optional): instance identifier to use when multiple plugin instances are initializedsecretRef(optional): secret path, optionally with#KEYto override the extracted key. If omitted, usesdefaultPath. Use#key(without a path) to override the inferred key while still usingdefaultPath.
Named args:
key(optional): JSON key to extract from the secret (overrides#KEYsyntax and item key default)raw(optional): set totrueto return all key/value pairs as a JSON blob instead of extracting a single key. Useful with@setValuesBulkfor bulk loading.
Key extraction: By default, the item key (variable name) is used as the JSON key. Override with #KEY or key=, or use raw=true to get everything.
How paths work:
Vault KV v2 stores key/value pairs at a path. Given a path like secret/myapp/config, the plugin calls GET /v1/secret/data/myapp/config (the first path segment is the mount point, and /data/ is inserted for the KV v2 API).
# Uses defaultPath, extracts item keyDATABASE_URL=vaultSecret()
# Override inferred key using # syntax (still uses defaultPath)STRIPE_KEY=vaultSecret("#stripe_api_key")
# Explicit path, extracts item key "DB_HOST"DB_HOST=vaultSecret("secret/db/config")
# Override key with # syntaxDB_PASSWORD=vaultSecret("secret/db/config#password")
# Extract key (named parameter)DB_PORT=vaultSecret("secret/db/config", key="PORT")
# Fetch full secret as JSONDB_CONFIG=vaultSecret("secret/db/config", raw=true)
# With instance IDPROD_SECRET=vaultSecret(prod, "secret/api/keys")Troubleshooting
Section titled “Troubleshooting”Secret not found
Section titled “Secret not found”- Verify the secret exists:
vault kv get secret/myapp/config - Check the mount point is correct (first path segment, typically
secret) - Ensure you’re using KV v2, not KV v1 (different API format)
Permission denied
Section titled “Permission denied”- Check your token’s policies:
vault token lookup - Ensure your policy includes
readcapability onsecret/data/*(note the/data/prefix for KV v2) - For AppRole: verify the role has the correct policies attached
Authentication failed
Section titled “Authentication failed”- Local dev: Run
vault login(orbao loginfor OpenBao) and ensureVAULT_ADDRis set correctly - CI/CD: Verify your token or AppRole credentials are properly wired up in
@initHcpVault() - Check if the token has expired:
vault token lookup - For AppRole: verify the secret ID hasn’t expired and generate a new one if needed
JSON key not found
Section titled “JSON key not found”- Verify the key exists at the path:
vault kv get -field=MY_KEY secret/myapp/config - Key names are case-sensitive
- Check available keys:
vault kv get secret/myapp/config