Vault is optional — server falls back to bcrypt (Phase 1 behaviour) when VAULT_ADDR is not set. Full coexistence: existing bcrypt credentials continue to work until rotated. Changes: - src/vault/VaultClient.ts — wraps node-vault KV v2; writeSecret, readSecret, verifySecret (constant-time), deleteSecret - src/db/migrations/005_add_vault_path.sql — vault_path column on credentials - CredentialRepository — createWithVaultPath, updateVaultPath methods - CredentialService — routes generate/rotate through Vault when configured; bcrypt path unchanged - OAuth2Service — verifies via Vault when vaultPath set, bcrypt otherwise - src/app.ts — createVaultClientFromEnv() wired into service layer - ICredentialRow — vaultPath field added - docs/devops/environment-variables.md — VAULT_ADDR, VAULT_TOKEN, VAULT_MOUNT - docs/devops/vault-setup.md — dev quickstart, production config, migration guide - tests: 33/33 unit tests pass (VaultClient + CredentialService Vault path) - node-vault + @types/node-vault installed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.5 KiB
HashiCorp Vault Setup
Phase 2 of AgentIdP optionally stores credential secrets in HashiCorp Vault KV v2 instead of bcrypt hashes in PostgreSQL. This guide covers:
- Dev mode quickstart
- Production Vault configuration
- Migration from bcrypt to Vault
Vault is entirely optional. If VAULT_ADDR is not set, AgentIdP operates in bcrypt mode (identical to Phase 1 behaviour).
How Vault integration works
When enabled:
POST /api/v1/agents/{agentId}/credentials— the plain-text secret is written to Vault at{mount}/data/agentidp/agents/{agentId}/credentials/{credentialId}. Only the Vault path is stored in PostgreSQL (credentials.vault_path). No bcrypt hash is written.POST /api/v1/token— the submittedclient_secretis compared against the value read from Vault (constant-time comparison). No bcrypt is involved.POST /api/v1/agents/{agentId}/credentials/{credentialId}/rotate— a new Vault version is written (KV v2 versioning). The path is unchanged; the old version is retained in Vault history.DELETE /api/v1/agents/{agentId}/credentials/{credentialId}— all versions of the secret are permanently deleted from Vault.
Coexistence: Credentials created before Vault was enabled keep their bcrypt hash and continue to work. New credentials use Vault. Both paths coexist until all pre-Vault credentials are rotated.
Dev mode quickstart
The fastest way to get Vault running locally:
# Pull and start Vault in dev mode (in-memory, auto-unsealed)
docker run --rm -d \
--name vault-dev \
-p 8200:8200 \
-e VAULT_DEV_ROOT_TOKEN_ID=dev-root-token \
hashicorp/vault:1.15 server -dev
# Verify it is running
curl http://127.0.0.1:8200/v1/sys/health | jq .
Add to your .env:
VAULT_ADDR=http://127.0.0.1:8200
VAULT_TOKEN=dev-root-token
VAULT_MOUNT=secret
The KV v2 secrets engine is automatically enabled at secret/ in dev mode. No further configuration is needed.
Warning
: Dev mode stores everything in memory. Data is lost when the container stops. Do not use dev mode in production.
Production Vault configuration
1. Enable KV v2 secrets engine
vault secrets enable -path=secret kv-v2
Or use a custom mount path:
vault secrets enable -path=agentidp kv-v2
# Set VAULT_MOUNT=agentidp in your .env
2. Create a policy for AgentIdP
# agentidp-policy.hcl
path "secret/data/agentidp/*" {
capabilities = ["create", "read", "update", "delete"]
}
path "secret/metadata/agentidp/*" {
capabilities = ["delete"]
}
Apply the policy:
vault policy write agentidp agentidp-policy.hcl
3. Create a service token
vault token create \
-policy=agentidp \
-ttl=8760h \
-renewable=true \
-display-name="agentidp-service"
Copy the token field from the output and set it as VAULT_TOKEN in your environment.
4. Token renewal
Service tokens expire unless renewed. Set up a scheduled renewal before the TTL expires:
# Renew with a new 720-hour (30-day) lease
vault token renew -increment=720h <token>
In Kubernetes, use Vault Agent Injector or the Vault Secrets Operator to handle renewal automatically.
Running migration 005
After configuring Vault, run the migration to add the vault_path column:
npm run db:migrate
Verify the migration:
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = 'credentials'
ORDER BY ordinal_position;
You should see a vault_path column with data_type = text and is_nullable = YES.
Migrating existing credentials to Vault
Existing credentials (with vault_path IS NULL) continue to work via bcrypt until they are rotated. To migrate a credential:
# Rotate the credential — this writes the new secret to Vault
curl -s -X POST http://localhost:3000/api/v1/agents/$AGENT_ID/credentials/$CRED_ID/rotate \
-H "Authorization: Bearer $TOKEN" | jq .
The response includes the new clientSecret (store it immediately). After rotation, vault_path is set and the bcrypt hash is cleared.
To migrate all credentials for an agent in bulk, rotate them one by one using the API.
Verifying Vault secrets
After generating a credential with Vault enabled, verify the secret was written:
vault kv get secret/agentidp/agents/$AGENT_ID/credentials/$CRED_ID
Expected output:
====== Secret Path ======
secret/data/agentidp/agents/<agentId>/credentials/<credentialId>
======= Metadata =======
Key Value
--- -----
created_time 2026-03-28T...
version 1
====== Data ======
Key Value
--- -----
clientSecret <the secret>
Troubleshooting
VAULT_WRITE_ERROR on credential generation
- Verify Vault is running:
curl $VAULT_ADDR/v1/sys/health - Verify the token has write access:
vault token capabilities $VAULT_TOKEN secret/data/agentidp/test - Check Vault audit logs:
vault audit list
VAULT_READ_ERROR on token issuance
- Verify the
vault_pathstored in PostgreSQL matches the actual Vault path - Check the token has read access to
secret/data/agentidp/*
Vault is down — what happens?
If Vault is unreachable, credential generation and token issuance for Vault-backed credentials will fail with a 500 error. Credentials created before Vault was enabled (bcrypt mode) continue to work.
For high availability, run Vault in HA mode with an integrated Raft storage backend. See Vault HA documentation.