- 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>
14 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.
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
# ── 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.