Files
sentryagent-idp/docs/devops/environment-variables.md
SentryAgent.ai Developer f9a6a8aafb docs(devops): update all documentation for DockerSpec compliance
- 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>
2026-04-08 08:27:37 +00:00

565 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](security.md) 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](security.md)).
---
### `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](security.md) |
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`, and `STRIPE_PRICE_ID` are
> required when `BILLING_ENABLED=true`. For local development, set `BILLING_ENABLED=false` and
> 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-setup.md).
---
### `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 `.env` to version control. Add it to `.gitignore`.
---
## Variable Validation at Startup
The application validates required variables at startup in this order:
1. `JWT_PRIVATE_KEY` and `JWT_PUBLIC_KEY` — checked in `createApp()` before the server starts
2. `DATABASE_URL` — checked when `getPool()` is first called (during `createApp()`)
3. `REDIS_URL` — checked when `getRedisClient()` is first called (during `createApp()`)
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_ENABLED` and `COMPLIANCE_ENABLED`
> determine whether their respective routers are mounted — changing these values requires a
> process restart.