- Replace all docker-compose.yml/docker-compose.monitoring.yml references with compose.yaml/compose.monitoring.yaml (modern Compose Spec naming) - Replace all `docker-compose` CLI commands with `docker compose` (plugin syntax) - Update Dockerfile stage descriptions: node:18-alpine → node:20.11-bookworm-slim, built-in node user → explicit nodeapp:1001 non-root user - Update image version references: postgres:14-alpine → postgres:14.12-alpine3.19, redis:7-alpine → redis:7.2-alpine3.19 - Externalize postgres credentials: hardcoded values → POSTGRES_USER/PASSWORD/DB env vars - Externalize Grafana admin password: hardcoded 'agentidp' → GF_ADMIN_PASSWORD env var - Add Docker Compose Variables section to environment-variables.md (POSTGRES_*, GF_ADMIN_PASSWORD) - Update local-development.md Step 3: cp .env.example .env, document POSTGRES_* purpose - Update quick-start.md: cp .env.example .env, use awk/sed for JWT key injection - Update 07-dev-setup.md: remove 'no .env.example' claim, reference cp .env.example - Update docker-compose.yml key file description in 04-codebase-structure.md - Update monitoring overlay launch commands across all docs (compose.yaml + compose.monitoring.yaml) - Update volume names to kebab-case: postgres_data → postgres-data, redis_data → redis-data - Fix compliance encryption-runbook: docker-compose restart agentidp → docker compose restart app All docs now consistent with compose.yaml in repo root. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
16 KiB
Environment Variables
Complete reference for all environment variables consumed by AgentIdP.
Variables are loaded from a .env file at startup via dotenv. In production, inject them directly into the process environment — do not commit .env to version control.
Docker Compose Variables
These variables are read by compose.yaml — not by the application itself. They are required when running the stack via docker compose up.
POSTGRES_USER
PostgreSQL superuser name — used to configure the postgres container and construct DATABASE_URL.
| Required for Compose | Yes |
Default in .env.example |
sentryagent |
| Example | POSTGRES_USER=sentryagent |
POSTGRES_PASSWORD
PostgreSQL superuser password.
| Required for Compose | Yes |
Default in .env.example |
change-me-in-production |
| Example | POSTGRES_PASSWORD=strongpassword |
Never use the default value in production. Generate a strong random password.
POSTGRES_DB
PostgreSQL database name to create on first startup.
| Required for Compose | Yes |
Default in .env.example |
sentryagent_idp |
| Example | POSTGRES_DB=sentryagent_idp |
GF_ADMIN_PASSWORD
Grafana admin panel password — used by compose.monitoring.yaml.
| Required for monitoring stack | Yes |
Default in .env.example |
change-me-in-production |
| Example | GF_ADMIN_PASSWORD=strongpassword |
Never use the default value in production.
Required Variables
These variables must be set. The server will throw and exit immediately if any are missing.
DATABASE_URL
PostgreSQL connection string.
| Required | Yes |
| Format | postgresql://<user>:<password>@<host>:<port>/<database> |
| Example | postgresql://sentryagent:sentryagent@localhost:5432/sentryagent_idp |
The application uses pg.Pool with this connection string. Pool sizing is controlled by the optional DB_POOL_* variables documented below.
REDIS_URL
Redis connection URL.
| Required | Yes |
| Format | redis://<host>:<port> or redis://<user>:<password>@<host>:<port> |
| Example | redis://localhost:6379 |
Used for token revocation, rate limiting, and monthly token counters.
JWT_PRIVATE_KEY
PEM-encoded RSA-2048 private key for signing JWT access tokens (RS256).
| Required | Yes |
| Format | PEM string, including -----BEGIN RSA PRIVATE KEY----- header and footer |
| Example | See Security guide for key generation |
In a .env file, use double quotes and encode newlines as \n:
JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEow...\n-----END RSA PRIVATE KEY-----"
Alternatively, read from a file at startup (see Security guide).
JWT_PUBLIC_KEY
PEM-encoded RSA-2048 public key for verifying JWT access tokens.
| Required | Yes |
| Format | PEM string, including -----BEGIN PUBLIC KEY----- header and footer |
| Example | Derived from JWT_PRIVATE_KEY — see Security guide |
Every authenticated request verifies the JWT signature using this key. If this key does not match the private key used to sign tokens, all authentication will fail.
Note on Billing:
STRIPE_SECRET_KEY,STRIPE_WEBHOOK_SECRET, andSTRIPE_PRICE_IDare required whenBILLING_ENABLED=true. For local development, setBILLING_ENABLED=falseand use placeholder values.
Optional Variables
These variables have defaults and do not need to be set for local development.
VAULT_ADDR
HashiCorp Vault server address. Required to enable Vault integration (Phase 2).
| Required | No (Vault is optional) |
| Format | URL string |
| Example | VAULT_ADDR=http://127.0.0.1:8200 |
When set alongside VAULT_TOKEN, new credentials are stored in Vault KV v2 instead of as bcrypt hashes in PostgreSQL. Existing bcrypt credentials continue to work unchanged until rotated. See Vault setup guide.
VAULT_TOKEN
Vault authentication token. Required when VAULT_ADDR is set.
| Required | Only when VAULT_ADDR is set |
| Format | String |
| Example | VAULT_TOKEN=hvs.XXXXXXXXXXXXXXXXXXXXXX |
Use a Vault service token scoped to read, write, and delete on {VAULT_MOUNT}/data/agentidp/* and {VAULT_MOUNT}/metadata/agentidp/*.
VAULT_MOUNT
KV v2 secrets engine mount path.
| Required | No |
| Default | secret |
| Format | String (no leading or trailing slash) |
| Example | VAULT_MOUNT=agentidp |
BILLING_ENABLED
| Required | No |
| Default | false |
| Values | true, false |
| Example | BILLING_ENABLED=false |
Gates Stripe billing integration and free-tier agent limit enforcement. When false, no Stripe
API calls are made and all tier limits are unenforced. Set to false for in-house testing.
STRIPE_SECRET_KEY
| Required | Only when BILLING_ENABLED=true |
| Format | Stripe secret key string (sk_live_* or sk_test_*) |
| Example | STRIPE_SECRET_KEY=sk_test_placeholder |
Stripe API key used to create Checkout Sessions for tier upgrades. Never use a live key in development.
STRIPE_WEBHOOK_SECRET
| Required | Only when BILLING_ENABLED=true |
| Format | Stripe webhook signing secret (whsec_*) |
| Example | STRIPE_WEBHOOK_SECRET=whsec_placeholder |
Used to verify the HMAC signature on incoming Stripe webhook events. Without this, the billing webhook endpoint will reject all events.
STRIPE_PRICE_ID
| Required | Only when BILLING_ENABLED=true |
| Format | Stripe Price ID string (price_*) |
| Example | STRIPE_PRICE_ID=price_placeholder |
The Stripe Price object used when creating a Checkout Session for the Pro tier upgrade.
ANALYTICS_ENABLED
| Required | No |
| Default | true |
| Values | true, false |
| Example | ANALYTICS_ENABLED=true |
Feature flag that gates the /api/v1/analytics/* routes. When false, the analytics router is
not mounted and all analytics endpoints return 404. Events are still recorded internally
regardless of this flag.
TIER_ENFORCEMENT
| Required | No |
| Default | true |
| Values | true, false |
| Example | TIER_ENFORCEMENT=true |
Enables Redis-backed tier limit enforcement per tenant. When true, the tierEnforcement
middleware checks daily API call and token counts against per-tier limits defined in
src/config/tiers.ts. Enterprise tenants with maxCallsPerDay: Infinity bypass enforcement.
When false, no tier limits are enforced.
COMPLIANCE_ENABLED
| Required | No |
| Default | true |
| Values | true, false |
| Example | COMPLIANCE_ENABLED=true |
Feature flag that gates the report and agent-card export endpoints under
/api/v1/compliance/*. When false, those endpoints return 404. The SOC2 controls endpoint
(/api/v1/compliance/controls) and audit chain verification (/api/v1/audit/verify) are
always enabled regardless of this flag.
REDIS_RATE_LIMIT_ENABLED
| Required | No |
| Default | false |
| Values | true, false |
| Example | REDIS_RATE_LIMIT_ENABLED=true |
When true, rate limiting uses a Redis-backed sliding-window counter per client_id. When
false, rate limiting uses an in-process RateLimiterMemory store (does not share state
across multiple app instances).
RATE_LIMIT_WINDOW_MS
| Required | No |
| Default | 60000 |
| Format | Integer (milliseconds) |
| Example | RATE_LIMIT_WINDOW_MS=60000 |
Duration of the sliding-window rate limit period in milliseconds. Only effective when
REDIS_RATE_LIMIT_ENABLED=true.
RATE_LIMIT_MAX_REQUESTS
| Required | No |
| Default | 100 |
| Format | Integer |
| Example | RATE_LIMIT_MAX_REQUESTS=100 |
Maximum number of requests allowed per client_id within RATE_LIMIT_WINDOW_MS. Requests
exceeding this limit receive 429 RATE_LIMIT_EXCEEDED.
DB_POOL_MAX
| Required | No |
| Default | 20 |
| Format | Integer |
| Example | DB_POOL_MAX=20 |
Maximum number of PostgreSQL connections in the pool. Increase for high-throughput production
deployments. Ensure your PostgreSQL instance's max_connections is set to at least
DB_POOL_MAX × number_of_app_instances + 5.
DB_POOL_MIN
| Required | No |
| Default | 2 |
| Format | Integer |
| Example | DB_POOL_MIN=2 |
Minimum number of idle connections kept alive in the pool.
DB_POOL_IDLE_TIMEOUT_MS
| Required | No |
| Default | 30000 |
| Format | Integer (milliseconds) |
| Example | DB_POOL_IDLE_TIMEOUT_MS=30000 |
Milliseconds a connection can sit idle before being evicted from the pool.
DB_POOL_CONNECTION_TIMEOUT_MS
| Required | No |
| Default | 5000 |
| Format | Integer (milliseconds) |
| Example | DB_POOL_CONNECTION_TIMEOUT_MS=5000 |
Milliseconds the pool waits for a connection to become available before throwing a connection timeout error.
VAULT_KV_MOUNT
| Required | No |
| Default | secret |
| Format | String (no leading or trailing slash) |
| Example | VAULT_KV_MOUNT=agentidp |
KV v2 secrets engine mount path used by VaultService. Equivalent to the existing VAULT_MOUNT
variable — note that .env.example uses VAULT_KV_MOUNT; the underlying service reads either.
OPA_URL
| Required | No |
| Format | URL string |
| Example | OPA_URL=http://localhost:8181 |
URL of a running OPA server for external policy evaluation. When unset, the application falls
back to the embedded Wasm or JSON policy in POLICY_DIR. Used for health check reporting.
KAFKA_BROKERS
| Required | No |
| Format | Comma-separated broker addresses |
| Example | KAFKA_BROKERS=localhost:9092 |
When set, the KafkaAdapter publishes domain events to Kafka. When unset, Kafka publishing is
disabled and events are only delivered via the WebhookService.
ENFORCE_TLS
| Required | No |
| Default | false |
| Values | true, false |
| Example | ENFORCE_TLS=true |
When true, the tlsEnforcementMiddleware redirects all HTTP requests to HTTPS. Enable in
production deployments where TLS termination is handled at the application layer.
POLICY_DIR
Directory containing OPA policy files (authz.rego, authz.wasm, data/scopes.json).
| Required | No |
| Default | <cwd>/policies |
| Format | Absolute or relative directory path |
| Example | POLICY_DIR=/etc/sentryagent/policies |
At startup the OPA authorization middleware loads ${POLICY_DIR}/authz.wasm (Wasm mode) if present; otherwise it loads ${POLICY_DIR}/data/scopes.json (fallback mode). Send SIGHUP to the process to hot-reload the policy files without a restart.
PORT
HTTP port the Express server listens on.
| Required | No |
| Default | 3000 |
| Format | Integer |
| Example | PORT=8080 |
NODE_ENV
Node.js environment flag.
| Required | No |
| Default | undefined (treated as development) |
| Values | development, test, production |
| Example | NODE_ENV=production |
Effect: When NODE_ENV=test, HTTP request logging (Morgan) is disabled.
CORS_ORIGIN
Allowed origin(s) for Cross-Origin Resource Sharing.
| Required | No |
| Default | * (all origins) |
| Format | URL string or * |
| Example | CORS_ORIGIN=https://app.mycompany.ai |
In production, set this to the specific origin(s) that should be permitted to call the API. The default * is acceptable for a public API but restricts cookie-based auth flows (not applicable here — Bearer tokens only).
Complete .env Example
# ── Server ──────────────────────────────────────────────────────────────────
NODE_ENV=development
PORT=3000
CORS_ORIGIN=http://localhost:3001
# ── Docker Compose (postgres container + monitoring) ─────────────────────────
POSTGRES_USER=sentryagent
POSTGRES_PASSWORD=change-me-in-production
POSTGRES_DB=sentryagent_idp
GF_ADMIN_PASSWORD=change-me-in-production
# ── Database ─────────────────────────────────────────────────────────────────
DATABASE_URL=postgresql://sentryagent:sentryagent@localhost:5432/sentryagent_idp
DB_POOL_MAX=20
DB_POOL_MIN=2
DB_POOL_IDLE_TIMEOUT_MS=30000
DB_POOL_CONNECTION_TIMEOUT_MS=5000
# ── Redis ────────────────────────────────────────────────────────────────────
REDIS_URL=redis://localhost:6379
REDIS_RATE_LIMIT_ENABLED=true
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX_REQUESTS=100
# ── JWT Keys (generate with openssl — see docs/devops/security.md) ──────────
JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEow...\n-----END RSA PRIVATE KEY-----"
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIj...\n-----END PUBLIC KEY-----"
# ── Billing (Stripe) — set BILLING_ENABLED=false for local/in-house testing ─
BILLING_ENABLED=false
STRIPE_SECRET_KEY=sk_test_placeholder
STRIPE_WEBHOOK_SECRET=whsec_placeholder
STRIPE_PRICE_ID=price_placeholder
# ── Phase 6 Feature Flags ─────────────────────────────────────────────────────
ANALYTICS_ENABLED=true
TIER_ENFORCEMENT=true
COMPLIANCE_ENABLED=true
# ── HashiCorp Vault (optional) ────────────────────────────────────────────────
# VAULT_ADDR=http://127.0.0.1:8200
# VAULT_TOKEN=hvs.XXXXXXXXXXXXXXXXXXXXXX
# VAULT_KV_MOUNT=secret
# ── OPA (optional) ───────────────────────────────────────────────────────────
# POLICY_DIR=/etc/sentryagent/policies
# OPA_URL=http://localhost:8181
# ── Kafka (optional) ─────────────────────────────────────────────────────────
# KAFKA_BROKERS=localhost:9092
# ── TLS ──────────────────────────────────────────────────────────────────────
# ENFORCE_TLS=true
Do not commit
.envto version control. Add it to.gitignore.
Variable Validation at Startup
The application validates required variables at startup in this order:
JWT_PRIVATE_KEYandJWT_PUBLIC_KEY— checked increateApp()before the server startsDATABASE_URL— checked whengetPool()is first called (duringcreateApp())REDIS_URL— checked whengetRedisClient()is first called (duringcreateApp())
If any required variable is missing, the process exits with an error before binding to any port.
Feature flags (
BILLING_ENABLED,ANALYTICS_ENABLED,TIER_ENFORCEMENT,COMPLIANCE_ENABLED) are read at startup.ANALYTICS_ENABLEDandCOMPLIANCE_ENABLEDdetermine whether their respective routers are mounted — changing these values requires a process restart.