Configuration
This page documents all environment variables and default settings for the Ironflow server and its SDKs.
Environment Variables
Section titled “Environment Variables”| Variable | Description | Default |
|---|---|---|
IRONFLOW_SERVER_URL | The URL SDKs and CLI use to connect. The Go agent SDK also honors IRONFLOW_URL as a fallback alias (checked first). | http://localhost:9123 |
IRONFLOW_API_KEY | The key used for authentication. | (None) |
IRONFLOW_DATABASE_URL | Postgres connection string (triggers PG mode). | (None, uses SQLite) |
IRONFLOW_MASTER_KEY | 32-byte hex key for secrets encryption. | (None, Dev Mode) |
IRONFLOW_JWT_SECRET | 32-byte hex key for dashboard JWT signing. Auto-generated and persisted to .ironflow_jwt_secret if not set. | (Auto-generated) |
IRONFLOW_SIGNING_KEY | Key for verifying push-mode webhooks. | (None) |
IRONFLOW_ENV | Default environment for CLI and SDK operations. | default |
IRONFLOW_CONFIG_DIR | Directory for storing platform credentials (credentials.json). Used by ironflow platform login. | ~/.config/ironflow |
NATS_STORE_DIR | Disk path for embedded NATS JetStream storage. | (Auto-derived from --db path: {db_dir}/ironflow-nats. See Embedded NATS storage below.) |
LOG_LEVEL | trace, debug, info, warn, error. Overrides per-command defaults. | warn (CLI), info (serve) |
LOG_FORMAT | text for human-readable output. Only affects serve command (JSON by default). CLI commands always use text. | text (CLI), JSON (serve) |
Embedded NATS storage
Section titled “Embedded NATS storage”ironflow serve runs an embedded NATS JetStream server when NATS_URL is not
set. JetStream needs a storage directory for streams and consumer state.
Default behavior (since #614): the store directory is auto-derived from the
SQLite database path. For the default ironflow.db it becomes ./ironflow-nats/;
for /data/ironflow.db it becomes /data/ironflow-nats/. The directory is
created at startup with 0700 permissions (owner read/write/exec only) because
JetStream stream files contain workflow event payloads that may include PII or
secrets. Existing directories are left as-is so operators can pre-create with
their preferred ownership and permissions.
Memory-mode opt-out: pass an explicit empty value for testing, demos, or ephemeral CI:
ironflow serve --nats-store-dir=""The explicit empty CLI flag wins over a NATS_STORE_DIR environment variable
inherited from the shell or systemd unit.
YAML-only opt-out is not currently supported. YAML round-trip cannot
distinguish nats.storeDir: "" from an omitted field, so a YAML config alone
cannot request memory mode. YAML operators who want ephemeral embedded NATS
have two options today: (1) point at an external NATS server via nats.url,
or (2) override at startup with --nats-store-dir="" on the command line. A
discoverable :memory: sentinel for --nats-store-dir and NATS_STORE_DIR
is tracked in TODOS.md as a follow-up.
Postgres mode: when IRONFLOW_DATABASE_URL is set and NATS_URL is NOT set
(embedded NATS), ironflow serve and ironflow validate refuse to start
without an explicit --nats-store-dir or NATS_STORE_DIR. Auto-deriving from
the SQLite path is not safe — there is no SQLite path in Postgres mode, and
silently creating a directory in the current working directory is dangerous
under systemd units, container WorkingDirectory: /, and read-only cwd. Set
the directory explicitly when you want embedded NATS with Postgres, or use
--nats-url for an external NATS server.
:memory: SQLite: same fail-fast as Postgres mode. --db :memory: plus
embedded NATS without an explicit store dir is rejected at startup.
Security note — secrets KV plaintext on disk. The secrets KV bucket is
backed by JetStream file storage. When IRONFLOW_MASTER_KEY is unset, secret
values are stored without encryption (passthrough). Before #614 this was
masked by memory-mode embedded NATS; with persistent NATS as the default,
secrets without a master key persist plaintext to {store_dir}/secrets/....
The server emits a startup WARN when this configuration is detected and a
stronger WARN with the count of plaintext secrets when any exist. Set
IRONFLOW_MASTER_KEY to a 32-byte hex value (e.g. openssl rand -hex 32) for
AES-256-GCM encryption, then re-set existing secrets via ironflow secret set
so they get re-encrypted.
Storage profile. File-storage streams retain about 1.3 GB total
(internal/nats/streams.go), versus about 75 MB total in memory mode.
Operators on size-constrained disks can override per-stream limits via YAML
or roll back to memory mode with --nats-store-dir="".
Operator discipline. The embedded NATS server does not lock its store
directory, so two ironflow serve processes against the same DB directory
will silently corrupt JetStream state. Run a single process per directory.
Observability
Section titled “Observability”| Variable | Description | Default |
|---|---|---|
IRONFLOW_OTEL_ENDPOINT | OTLP gRPC endpoint for OpenTelemetry tracing. | (None, disabled) |
IRONFLOW_OTEL_SAMPLE_RATE | Trace sampling rate (0.0 to 1.0). | 1.0 |
IRONFLOW_OTEL_SERVICE_NAME | OTel service name. | ironflow |
IRONFLOW_OTEL_INSECURE | Use plaintext gRPC for OTLP export. | true |
IRONFLOW_METRICS_ENABLED | Enable Prometheus metrics at /metrics. | false |
Profiling
Section titled “Profiling”The --pprof CLI flag on ironflow serve starts a separate listener on :6060 with Go’s net/http/pprof handlers for CPU, heap, and goroutine profiling. Not configured via environment variable — CLI flag only. See Benchmarks for the load testing workflow.
Clustering
Section titled “Clustering”| Variable | Description | Default |
|---|---|---|
NATS_URL | External NATS server URL. When set, connects to external NATS instead of embedded. Requires PostgreSQL. | (None, embedded) |
NATS_CREDS_FILE | NATS credentials file for NKey/JWT auth. | (None) |
IRONFLOW_NODE_ID | Unique node identifier for distributed coordination. Must be stable per node. | Random UUID v4 |
IRONFLOW_STALE_CLAIM_THRESHOLD | Duration before stale scheduler claims are reclaimed. | 2m |
IRONFLOW_STALE_CLAIM_RECOVERY_INTERVAL | How often the scheduler scans for orphaned claims. Lower values detect crashes faster at the cost of more frequent SQL sweeps. The --dev flag drops it to 5s. | 60s |
NATS_FILE_STORAGE | Set to true to use file-backed JetStream streams instead of memory. | false |
NATS_STREAM_REPLICAS | JetStream stream replica count for clustered NATS. | 1 |
Security
Section titled “Security”| Variable | Description | Default |
|---|---|---|
IRONFLOW_WS_ALLOWED_ORIGINS | Comma-separated allowed origins for WebSocket connections. When empty, all origins are allowed. | (None, all origins) |
IRONFLOW_AGENT_TOOLS_ALLOW_PRIVATE | When true, agent tool callback URLs may target RFC1918/loopback addresses. The --dev flag forces this on regardless. | false |
IRONFLOW_AGENT_TOOLS_URL_ALLOWLIST | Comma-separated host allowlist for agent tool callback URLs. When set, only listed hosts are accepted. | (None, no allowlist) |
Engine
Section titled “Engine”| Variable | Description | Default |
|---|---|---|
IRONFLOW_CANCEL_ON_REPLAY_WINDOW_SECONDS | Lookback window (seconds) for the cancel-on-replay buffer. Recent events newer than this are kept for cancellation matching. | 30 |
IRONFLOW_CANCEL_ON_REPLAY_BUFFER_SIZE | Maximum entries retained in the cancel-on-replay buffer. Bounds memory under high event rates. | 10000 |
Cloud Operator
Section titled “Cloud Operator”| Variable | Description | Default |
|---|---|---|
IRONFLOW_CAP_TOKEN_PRIVATE_KEY_B64 | Base64-encoded PKCS8 DER Ed25519 private key for minting deprovision capability tokens (ironflow cloud cap-token mint). | (None, required for mint) |
IRONFLOW_MASTER_KEY_B64 | Base64-encoded 32-byte meta-cluster KEK for decrypting break-glass bearer blobs and snapshot exports (ironflow cloud break-glass, ironflow cloud snapshot decrypt). | (None, required for decrypt) |
Auth Audit Batcher
Section titled “Auth Audit Batcher”| Variable | Description | Default |
|---|---|---|
IRONFLOW_AUDIT_BATCH_SIZE | Maximum rows per batch insert into the policy_decisions audit table. | 100 |
IRONFLOW_AUDIT_BATCH_FLUSH_MS | Milliseconds between batch flushes. Set lower for tighter audit latency; set higher for write throughput. | 1000 |
Projection Rebuild
Section titled “Projection Rebuild”| Variable | Description | Default |
|---|---|---|
IRONFLOW_REBUILD_BATCH_SIZE_SQLITE | Events per batch when rebuilding a projection against a SQLite-backed event store. | 500 |
IRONFLOW_REBUILD_BATCH_SIZE_POSTGRES | Events per batch when rebuilding a projection against a PostgreSQL-backed event store. | 1000 |
IRONFLOW_REBUILD_BATCH_PAUSE_MS | Milliseconds to pause between FULL rebuild batches (PG only). Useful for throttling large rebuilds. | 0 |
Bootstrap Defaults
Section titled “Bootstrap Defaults”On the very first boot of a fresh database, Ironflow creates a default admin user with the email admin@ironflow.local and a randomly generated password. Both the admin API key and password are printed to the console and must be saved immediately — they will not be shown again.
SDK Configuration
Section titled “SDK Configuration”TypeScript (Browser)
Section titled “TypeScript (Browser)”import { ironflow } from "@ironflow/browser";
ironflow.configure({ serverUrl: "http://localhost:9123", auth: { apiKey: "ifkey_..." }, environment: "default",});Additional options: transport (“connectrpc” | “websocket”), reconnect, visibility, logger, timeout.
TypeScript (Node.js)
Section titled “TypeScript (Node.js)”import { createClient } from "@ironflow/node";
const client = createClient({ serverUrl: process.env.IRONFLOW_SERVER_URL || "http://localhost:9123", apiKey: process.env.IRONFLOW_API_KEY,});Additional options: timeout (ms, default 30000), onError (global error handler).
API Payload Limits
Section titled “API Payload Limits”| Limit | Value | Applies To |
|---|---|---|
| Max Event Data | 512 KB | Payload of an emitted event. |
| Max Step Output | 1 MB | Data returned by a step.run. |
| Max Batch Size | 100 | Max events in a TriggerBatch call. |
| Wait Timeout | 30s | Default timeout for emit --wait. |