Environment variables
The node reads three kinds of environment variable:
- Secrets, which have no config-file equivalent and must come from the environment or a secret manager.
- Config overrides,
NODE_-prefixed, which take precedence over the same field inconfig.yaml. NODE_CONFIG, which selects the config file itself.
The override list is a hand-maintained allowlist, not a mechanical mapping of
every YAML field. Several blocks — zs.min_charge, zs.oracle,
zs.rate_limits, zs.tool_pricing, zs.signing_balance,
zs.coordinates, zs.payer_slot_cap, and everything under zs.models[] — are
file-only. Setting a plausible-looking NODE_ZS_… name for one of those does
nothing and reports nothing. If a setting isn't in a table below, put it in the
file.
zs-node doctor lists any NODE_* variable that is overriding your file at
runtime, which is the fastest way to catch a stale export.
Secrets — environment only
Never put these in config.yaml.
| Variable | What it is |
|---|---|
<NAME>_MNEMONIC | The signing account's 25-word mnemonic. Any variable ending in _MNEMONIC is picked up — the label before the suffix is informational, and lookup is by derived address. OPERATOR_SIGNING_MNEMONIC is the conventional name. The node refuses to start without a mnemonic matching zs.signing_addr. |
ZS_MNEMONIC_URLS | Comma-separated name=url pairs pointing at a cloud secret manager, in place of the above. Schemes: awssecretsmanager://, awsparamstore://, gcpsecretmanager://, azurekeyvault://, file://. Each backend uses its standard credential discovery (IAM role, ADC, managed identity). |
NODE_LLM_OPENAI_API_KEY | The text upstream's API key. Required (non-empty) for provider: openai_passthrough even when your backend needs no authentication — export a placeholder if so. For kronk under any authorization mode except open, this must be an admin token. |
NODE_IMAGE_LLM_OPENAI_API_KEY | The image upstream's API key for image_llm.provider: openai_passthrough. Independent of the text key, and it overrides image_llm.openai.api_key if both are set. |
NODE_LLM_COMFYUI_CLOUD_API_KEY | Comfy Cloud API key. Required when image_llm.provider: comfyui_cloud; startup refuses an empty one. |
NODE_ALGOD_TOKEN | Algod API token, when your provider needs one. |
Config file selection
| Variable | Effect |
|---|---|
NODE_CONFIG | Path to the YAML config file. The --config flag wins over it; with neither set the node loads ./config.yaml. |
server
| Variable | Overrides |
|---|---|
NODE_SERVER_LISTEN | server.listen — the public bind address. Defaults to loopback; a serving node needs :9090. |
NODE_SERVER_PRIVATE_LISTEN | server.private_listen — /healthz, /livez, /metrics. Empty string colocates them on the public port. |
NODE_SERVER_READ_TIMEOUT | server.read_timeout |
NODE_SERVER_WRITE_TIMEOUT | server.write_timeout — must stay 0 for streaming. |
NODE_SERVER_IDLE_TIMEOUT | server.idle_timeout |
NODE_SERVER_SSE_KEEPALIVE_INTERVAL | server.sse_keepalive_interval |
NODE_SERVER_DRAIN_GRACE | server.drain_grace |
NODE_SERVER_DRAIN_TIMEOUT | server.drain_timeout |
NODE_SERVER_SHUTDOWN_TIMEOUT | server.shutdown_timeout |
TLS and IP sync
| Variable | Overrides |
|---|---|
NODE_SERVER_TLS_MODE | server.tls.mode — off | manual | acme |
NODE_SERVER_TLS_MANUAL_CERT_PATH / _KEY_PATH | server.tls.manual.* |
NODE_SERVER_TLS_ACME_DOMAINS | server.tls.acme.domains (comma-separated) |
NODE_SERVER_TLS_ACME_EMAIL | server.tls.acme.email |
NODE_SERVER_TLS_ACME_CACHE_DIR | server.tls.acme.cache_dir |
NODE_SERVER_TLS_ACME_DIRECTORY_URL | server.tls.acme.directory_url |
NODE_SERVER_TLS_ACME_PROPAGATION_DELAY / _TIMEOUT | server.tls.acme.propagation_* |
NODE_SERVER_TLS_IP_SYNC_ENABLED | server.tls.ip_sync.enabled |
NODE_SERVER_TLS_IP_SYNC_IPV4 / _IPV6 | server.tls.ip_sync.ipv4 / .ipv6 |
NODE_SERVER_TLS_IP_SYNC_INTERVAL | server.tls.ip_sync.interval |
NODE_SERVER_TLS_IP_SYNC_TTL | server.tls.ip_sync.ttl |
NODE_SERVER_TLS_IP_SYNC_URL_ENABLED | server.tls.ip_sync.url.enabled |
algod and logging
| Variable | Overrides |
|---|---|
NODE_ALGOD_NETWORK | algod.network — localnet | testnet | mainnet |
NODE_ALGOD_ENDPOINT | algod.endpoint |
NODE_ALGOD_TOKEN | algod.token (see Secrets) |
NODE_LOGGING_LEVEL | logging.level |
NODE_LOGGING_FORMAT | logging.format |
llm — text backend
| Variable | Overrides |
|---|---|
NODE_LLM_PROVIDER | llm.provider |
NODE_LLM_OPENAI_BASE_URL | llm.openai.base_url |
NODE_LLM_OPENAI_TIMEOUT | llm.openai.timeout — must stay 0 for streaming. |
NODE_LLM_OPENAI_TRANSLATE_RESPONSES_TO_CHAT | llm.openai.translate_responses_to_chat |
NODE_LLM_OPENAI_MAX_RETRIES | llm.openai.max_retries |
NODE_LLM_OPENAI_RETRY_BASE_DELAY / _RETRY_MAX_DELAY / _RETRY_TOTAL_CAP | the transient-error retry budget |
NODE_LLM_OPENAI_LOG_RAW_USAGE | llm.openai.log_raw_usage — privacy-safe; counts only. |
NODE_LLM_OPENAI_DEBUG_DUMP_ERRORS | llm.openai.debug_dump_errors — dev only; writes decrypted content to stderr and is a hard startup error under tee.mode != none. See Privacy & non-retention. |
NODE_LLM_HEALTH_CHECK_INTERVAL / _TIMEOUT / _FAILURE_THRESHOLD | llm.health_check.* |
NODE_LLM_LOCAL_BINARY_PATH | llm.local.binary_path |
NODE_LLM_LOCAL_HOST / _PORT | llm.local.host / .port |
NODE_LLM_LOCAL_PARALLEL_SLOTS | llm.local.parallel_slots |
NODE_LLM_LOCAL_STARTUP_TIMEOUT | llm.local.startup_timeout |
NODE_LLM_LOCAL_VERBOSE_LLAMA | verbose llama-server output |
NODE_LLM_VERTEXAI_PROJECT / _LOCATION / _ENDPOINT / _TIMEOUT | llm.vertexai.* |
llm.local.models[] and llm.vertexai.models[] are file-only.
image_llm — image backend
| Variable | Overrides |
|---|---|
NODE_IMAGE_LLM_PROVIDER | image_llm.provider |
NODE_IMAGE_LLM_OPENAI_BASE_URL | image_llm.openai.base_url |
NODE_IMAGE_LLM_COMFYUI_BASE_URL | image_llm.comfyui.base_url |
NODE_IMAGE_LLM_COMFYUI_DATA_DIR | image_llm.comfyui.data_dir |
NODE_IMAGE_LLM_COMFYUI_CLOUD_BASE_URL | image_llm.comfyui_cloud.base_url |
zs — identity, admission, settlement
| Variable | Overrides |
|---|---|
NODE_ZS_OPERATOR_ID | zs.operator_id |
NODE_ZS_NODE_ID | zs.node_id |
NODE_ZS_OWNER_ADDR / NODE_ZS_SIGNING_ADDR | the on-chain address overrides |
NODE_ZS_ESCROW_APP_ID | zs.escrow_app_id |
NODE_ZS_NFD_APP_ID / NODE_ZS_NFD_API_URL | zs.nfd_app_id / zs.nfd.api_url |
NODE_ZS_NFD_RECORD_NAME | zs.nfd_record_name |
NODE_ZS_MAX_ACTIVE_TICKETS | zs.max_active_tickets |
NODE_ZS_TICKET_TTL | zs.ticket_ttl |
NODE_ZS_DEFAULT_EXPIRES_AFTER | zs.default_expires_after |
NODE_ZS_MEMPOOL_POLL_TIMEOUT / _INTERVAL | zs.mempool_poll_* |
NODE_ZS_SETTLEMENT_DB_PATH | zs.settlement_db_path |
NODE_ZS_SETTLEMENT_WATCHDOG_SECONDS | zs.settlement_watchdog_seconds |
NODE_ZS_SETTLEMENT_LAPSE_GRACE_SECONDS | zs.settlement_lapse_grace_seconds |
NODE_ZS_SETTLEMENT_RETENTION / _RETENTION_INTERVAL | the ledger janitor |
NODE_ZS_RELAY_ONLY | zs.relay_only |
NODE_ZS_ALLOW_PRIVATE_RELAY_TARGETS | zs.allow_private_relay_targets — localnet/dev only |
NODE_ZS_INJECT_SAFETY_IDENTIFIER | zs.inject_safety_identifier |
NODE_ZS_SELF_EVICTION_ENABLED / _INTERVAL / _THRESHOLD | zs.self_eviction.* |
zs.reserve
| Variable | Overrides |
|---|---|
NODE_ZS_RESERVE_ENFORCE_INPUT_BUDGET | zs.reserve.enforce_input_budget |
NODE_ZS_RESERVE_INPUT_BUDGET_TOLERANCE | zs.reserve.input_budget_tolerance |
NODE_ZS_RESERVE_TOOL_HEADROOM_PER_ITERATION | zs.reserve.tool_headroom_per_iteration |
zs.builtin_tools
| Variable | Overrides |
|---|---|
NODE_ZS_BUILTIN_TOOLS_ENABLED | the whole subsystem |
NODE_ZS_BUILTIN_TOOLS_MAX_ITERATIONS | max_iterations |
NODE_ZS_BUILTIN_TOOLS_MAX_STALLED_ITERATIONS | max_stalled_iterations |
NODE_ZS_BUILTIN_TOOLS_WEB_SEARCH_ENABLED / _MAX_RESULTS / _SAFE_SEARCH / _TIMEOUT | web_search.* |
NODE_ZS_BUILTIN_TOOLS_WEB_READ_ENABLED / _MAX_BYTES / _MAX_DOWNLOAD / _MAX_CONCURRENT / _TIMEOUT | web_read.* |
NODE_ZS_BUILTIN_TOOLS_WEB_READ_ALLOW_PRIVATE_TARGETS | web_read.allow_private_targets — localnet/dev only |
NODE_ZS_BUILTIN_TOOLS_FAVICONS_ENABLED / _TIMEOUT / _MAX_BYTES / _CACHE_MAX_BYTES / _CACHE_TTL | favicons.* |
NODE_ZS_BUILTIN_TOOLS_FAVICONS_ALLOW_PRIVATE_TARGETS | favicons.allow_private_targets — localnet/dev only |
NODE_ZS_BUILTIN_TOOLS_IMAGE_SEARCH_ENABLED / _MAX_RESULTS / _SAFE_SEARCH / _TIMEOUT | image_search.* — the tool itself is force-disabled in code regardless. |
tee
| Variable | Overrides |
|---|---|
NODE_TEE_MODE | tee.mode |
NODE_TEE_ATTESTATION_NRAS_URL | tee.attestation.nras_url |
NODE_TEE_ATTESTATION_REFRESH_INTERVAL | tee.attestation.refresh_interval |
NODE_TEE_ATTESTATION_EVIDENCE_CACHE_PATH | tee.attestation.evidence_cache_path |
NODE_TEE_ATTESTATION_PCCS_URL | tee.attestation.pccs_url |
tee.dataflow deliberately has no environment form — see Confidential
compute.
Where to put them
Under systemd, zs-node install-service drops a 0600
/etc/zerosignal/secrets.env referenced by the unit's EnvironmentFile=. Under
Docker, use --env-file or explicit -e flags. A common failure is exporting a
variable in your interactive shell and expecting the service to see it — see
Troubleshooting.