z4j reads settings from env vars (primary). See env vars for the exhaustive reference; this page groups the operator-facing settings by topic for skimming.
| Setting |
Default |
Notes |
Z4J_DATABASE_URL |
- (required) |
postgresql+asyncpg://user:pw@host/db |
Z4J_DATABASE_STATEMENT_CACHE_SIZE |
50 |
Per-connection asyncpg prepared-statement cache cap. 0 disables. |
Z4J_DATABASE_MAX_INACTIVE_CONNECTION_LIFETIME_SECONDS |
60 |
SQLAlchemy pool_recycle. Shorter values rotate per-connection caches faster under sustained load. |
Z4J_AUTO_MIGRATE |
true |
Run Alembic head migrations on brain boot. Set false for orchestrators that handle migrations separately. |
Z4J_STARTUP_VERIFY_LOCK_TIMEOUT_MS |
600000 |
How long a starting worker waits its turn for the audit-chain startup walk while sibling workers verify the same database. Scoped to that one transaction; the per-request Z4J_DB_LOCK_TIMEOUT_MS is unchanged. See scaling. |
The connection pool is configurable through Z4J_DATABASE_POOL_SIZE (default 20) and Z4J_DATABASE_MAX_OVERFLOW (default 10). On PostgreSQL the brain refuses to start when the total is below what its enabled leader-gated workers need, because a pool that cannot seat them deadlocks rather than degrades. See environment variables.
| Setting |
Default |
Notes |
Z4J_SECRET |
- (required) |
Master HMAC signing key. Derives the per-project frame HMAC key, hashes agent tokens, and encrypts stored TOTP secrets. It does not sign sessions (that is Z4J_SESSION_SECRET below) and does not sign the audit log (that is Z4J_AUDIT_CHAIN_SECRET). At least 32 bytes; 64 hex chars recommended. |
Z4J_SESSION_SECRET |
- (required) |
Session-cookie signing key, at least 32 bytes. Independent of Z4J_SECRET. |
Z4J_PUBLIC_URL |
http://localhost:7700 |
Full public URL (https://z4j.example.com). The default only passes validation when Z4J_ENVIRONMENT is exactly dev; outside dev the value must use https:// (or Z4J_ALLOW_HTTP_PUBLIC_URL must be set), so it is effectively required. Validated: no whitespace, no userinfo, http(s) only. |
Z4J_PREVIOUS_SECRETS |
- |
Comma-separated previous master secrets still accepted when verifying agent bearer tokens and decrypting stored secrets (TOTP secrets and notification channel configs). Writes use the new Z4J_SECRET. It does NOT cover frame signing, so rotation still requires re-credentialing every agent. Run z4j secrets rewrap to a clean report before dropping an entry; that re-encrypts every stored secret under the current master in one run. See incident response. |
Z4J_PREVIOUS_SESSION_SECRETS |
- |
Comma-separated previous session secrets still accepted during cookie-rotation. |
Z4J_AUDIT_CHAIN_SECRET |
- (required outside development) |
Dedicated audit-chain signing key, at least 32 bytes. Independent of Z4J_SECRET, with no fallback: the brain refuses to start without it outside development. |
Z4J_AUDIT_CHAIN_PREVIOUS_SECRETS |
- |
Comma-separated previous audit-chain keys still accepted by the verifier during rotation. Writes use the current Z4J_AUDIT_CHAIN_SECRET. |
Z4J_REDACTION_EXTRA_KEY_PATTERNS |
[] |
JSON array of extra key-name regexes the brain's redaction pass applies to every inbound event on top of the built-in patterns. Each entry is validated as a regex when settings load. See redaction. |
Z4J_SECRET, Z4J_SESSION_SECRET and Z4J_AUDIT_CHAIN_SECRET must each be at least 32 bytes, and so must every entry in the three previous-secret lists; a shorter value refuses to load. A URL-valued setting that is not itself a secret (Z4J_PUBLIC_URL, Z4J_EXPORT_SINK_S3_ENDPOINT_URL) refuses to load with user:password@ embedded in it, and the read-only settings page strips any such userinfo from every URL it renders.
The audit-log HMAC chain is signed with Z4J_AUDIT_CHAIN_SECRET, deliberately
separate from Z4J_SECRET. See HMAC audit chain
for why the separation matters and how activation works on an existing
deployment.
| Setting |
Default |
Notes |
Z4J_DASHBOARD_IP_ALLOWLIST |
[] |
CIDRs the login route and every session-cookie request must come from. |
Z4J_API_IP_ALLOWLIST |
[] |
CIDRs every Bearer API-key request must come from. Each key can carry its own allowed_cidrs on top; both must admit the request. |
Z4J_AGENT_IP_ALLOWLIST |
[] |
CIDRs the agent transports (WebSocket and long-poll) must come from. |
All three are JSON arrays of IPv4 or IPv6 CIDRs; a bare address means that
one host, and a malformed entry refuses to start. An empty list means no
restriction. The address matched is the one the brain resolved after
Z4J_TRUSTED_PROXIES, so an X-Forwarded-For header only counts when it
arrived from a declared proxy; from anywhere else the socket peer is what
gets matched. Loopback is never implicitly exempt: a list that omits
127.0.0.1/32 or ::1/128 refuses the brain's own host too. A refusal is
403 with error: "ip_denied", writes an auth.ip_denied audit row, and
counts in z4j_auth_ip_denied_total{surface}. See
threat model for what the
lists do and do not protect.
| Setting |
Default |
Notes |
Z4J_PASSWORD_MIN_LENGTH |
12 |
Default 12; configurable down to the supported floor of 8. |
Z4J_ARGON2_TIME_COST |
3 |
OWASP 2024 minimum. |
Z4J_ARGON2_MEMORY_COST |
65536 |
64 MiB, in KiB. |
Z4J_ARGON2_PARALLELISM |
4 |
Threads. |
SMTP servers are not configured via env vars. Each notification channel record carries its own smtp_host, smtp_port (default 587), smtp_user, smtp_pass, smtp_tls, from_addr and to_addrs fields. See notifications and smtp-presets.
| Setting |
Default |
Notes |
Z4J_RECONCILIATION_SWEEP_SECONDS |
300 |
Seconds between reconciliation passes (default 5 min). |
Z4J_RECONCILIATION_STALE_THRESHOLD_SECONDS |
900 |
Minimum age of a task still in started, pending or retry before it is eligible for reconciliation, measured from started_at (falling back to received_at, then created_at). 60 to 86400 (default 15 min). |
| Setting |
Default |
Notes |
Z4J_EVENT_RETENTION_DAYS |
30 |
Days raw events rows live; on PostgreSQL the partition worker drops the daily partitions past this age. The same value bounds the agent_status_history purge. |
Z4J_AUDIT_RETENTION_DAYS |
90 |
Days audit_log rows live before the retention worker or z4j audit prune removes them under the authenticated prune boundary. Minimum 1, no ceiling; above 3650 days the brain logs one startup WARNING and honours the value. Environment only, no write-back. See audit retention. |
Z4J_AUDIT_RETENTION_BY_CLASS |
{} |
JSON object mapping an action class (auth, command, schedule, ...) to its own window in days; unlisted classes use Z4J_AUDIT_RETENTION_DAYS. Validated when settings load. A longer class window holds every row written after the oldest row it keeps; a shorter one only reaches rows older than every retained neighbour, because the chain only loses a contiguous prefix. See audit retention. |
Z4J_AUDIT_CHAIN_VERIFY_ENABLED |
false |
Run the scheduled audit-chain verification worker. Off by default: verification walks every retained row, so an operator who has not asked for it does not pay for it. Leader-gated. |
Z4J_AUDIT_CHAIN_VERIFY_INTERVAL_SECONDS |
86400 |
Cadence for the scheduled verification. Floor 900 (15 minutes), ceiling 604800 (one week). |
Background audit exports of any size and the scheduled chain-head anchor; see audit exports for the full list of sink settings.
| Setting |
Default |
Notes |
Z4J_EXPORT_SINK |
none |
none, local (a directory, Z4J_EXPORT_SINK_PATH) or s3 (a bucket, Z4J_EXPORT_SINK_S3_BUCKET, through the optional z4j[s3] extra). none disables export jobs and the head export. |
Z4J_AUDIT_HEAD_EXPORT_INTERVAL_SECONDS |
0 |
Write the authenticated chain head to the sink on this cadence, under a stable key plus a dated copy, for z4j audit verify --known-head. 0 disables; otherwise 60 to 604800. Needs a sink. |
Off unless Z4J_AUDIT_WEBHOOK_URL is set. A leader-gated worker mirrors every audit row to the receiver from a durable cursor, at least once and in order; see audit webhook forwarding.
| Setting |
Default |
Notes |
Z4J_AUDIT_WEBHOOK_URL |
- |
Receiver URL; empty disables. Secret, because SIEM intake URLs embed tokens. |
Z4J_AUDIT_WEBHOOK_HMAC_SECRET |
- |
Signs every row. Required with the URL, at least 32 bytes. |
Z4J_AUDIT_WEBHOOK_BATCH_SIZE |
100 |
Rows per pass; a full batch runs the next pass at once. |
Z4J_AUDIT_WEBHOOK_POLL_INTERVAL_SECONDS |
5.0 |
Poll cadence once caught up. |
Z4J_AUDIT_WEBHOOK_MAX_BACKOFF_SECONDS |
300.0 |
Ceiling on the doubling wait after consecutive failures. |
| Setting |
Default |
Notes |
Z4J_METRICS_AUTH_TOKEN |
- (auto-minted on packaged SQLite) |
Bearer for /metrics. Auto-minted into ~/.z4j/secret.env only on the packaged SQLite bootstrap, whenever it is missing there; PostgreSQL deployments must set it explicitly or /metrics answers 401. z4j metrics-token prints; z4j metrics-token rotate rotates. |
Z4J_METRICS_PUBLIC |
false |
1 leaves /metrics open. Use only with a firewalled or proxy-authenticated endpoint. |
Sentry is opt-in: pip install "z4j[sentry]" and set Z4J_SENTRY_DSN; without a DSN it is off even when the SDK is installed. OpenTelemetry tracing works the same way with z4j[otel] and Z4J_OTEL_EXPORTER_OTLP_ENDPOINT. See the Sentry and OpenTelemetry sections of environment variables. Application logs go to stdout as JSON; ship them with Fluent Bit / Vector / Loki / Datadog.
| Setting |
Default |
Notes |
Z4J_BOOTSTRAP_ADMIN_EMAIL |
- |
Skip the setup URL and provision an admin automatically. |
Z4J_BOOTSTRAP_ADMIN_PASSWORD |
- |
Required with the email above. Eagerly popped from os.environ after use. |