Files
sentryagent-idp/docs/devops/vault-setup.md
SentryAgent.ai Developer 8cabc0191c docs: commit all Phase 6 documentation updates and OpenSpec archives
- devops docs: 8 files updated for Phase 6 state; field-trial.md added (946-line runbook)
- developer docs: api-reference (50+ endpoints), quick-start, 5 existing guides updated, 5 new guides added
- engineering docs: all 12 files updated (services, architecture, SDK guide, testing, overview)
- OpenSpec archives: phase-7-devops-field-trial, developer-docs-phase6-update, engineering-docs-phase6-update
- VALIDATOR.md + scripts/start-validator.sh: V&V Architect tooling added
- .gitignore: exclude session artifacts, build artifacts, and agent workspaces

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 02:24:24 +00:00

5.8 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:

  1. 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.
  2. POST /api/v1/token — the submitted client_secret is compared against the value read from Vault (constant-time comparison). No bcrypt is involved.
  3. 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.
  4. 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

Note: The .env.example file uses VAULT_KV_MOUNT as the variable name. The application reads both VAULT_KV_MOUNT and VAULT_MOUNT — prefer VAULT_KV_MOUNT in new configurations for consistency with the current .env.example.

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_path stored 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.