There is no single global HAL0_* → config-key mapping. Each variable below is read
by name at a specific call site. Most have a hardcoded fallback if unset.
Install layout
Variable
Default
Controls
HAL0_HOME
unset (production FHS paths)
Overrides all root paths for dev installs/tests: usr_lib, lib, etc, var_lib, var_log. See Paths & files.
HAL0_PREFIX
unset → $PWD/.hal0ai
Dev-prefix install root override, used by hal0 uninstall --dev.
HAL0_FHS_ROOT
unset → usr_lib()
FHS release-tree root override for hal0 doctor’s preflight-script lookup.
HAL0_PREFLIGHT_SH
unset (auto-discovered)
Explicit override path to preflight.sh.
HAL0_FORCE
unset
HAL0_FORCE=1 skips the hal0 uninstall --purge confirmation prompt.
HAL0_ALLOW_ROOT
unset (false)
1/true/yes allows the hal0-agent shim to run as root.
HAL0_AGENTS_CONF_DIR
/etc/hal0/agents
Directory of per-agent TOML files (<id>.toml) the hal0-agent shim reads for invocation overrides — type, home, venv, bin, port, host, serve_args.
Overrides the model-store root. Precedence: env → [models].store/effective_store() in hal0.toml → /var/lib/hal0/models.
HAL0_FLM_MODELS_DIR
unset
NPU/FLM model-store dir override. Precedence: env → [models].flm_store → /var/lib/hal0/.config/flm/models.
API / networking
Variable
Default
Controls
HAL0_PORT
8080
API port. Also read by hal0 serve --port.
HAL0_BIND_HOST
127.0.0.1 when unset; the installer seeds 0.0.0.0
API bind host. Also read by hal0 serve --host. Any value other than 127.0.0.1/localhost/::1 counts as LAN-bound: with an admin key configured, ADMIN-class routes then require an admin credential from non-loopback callers even while require_auth is off (#1822).
HAL0_HOSTNAME
socket.gethostname()
Canonical hostname override — feeds the dashboard URLs, the WS-origin allowlist, and the mDNS announcement. At install time (HAL0_HOSTNAME=jarvis sudo -E bash install.sh — the env path only; network.hostname in the answer file is consumed by the separate hal0 setup --answers process and does not reach the installer’s avahi sync yet, a known follow-up) the installer also pins avahi’s announced name via host-name= under [server] in /etc/avahi/avahi-daemon.conf, so http://jarvis.local:8080 resolves on the LAN without renaming the box (no hostnamectl). Re-running with a different name updates the announcement; running without one withdraws only the installer-managed line and reverts to the machine hostname. Caveat: if an operator had hand-written their own host-name= before pinning, the pin replaces it (with a warning) and a later no-override run does not restore it. Wrinkle: a renamed box stops answering to the hal0.local alias (avahi cannot cleanly publish CNAMEs) — the alias stays in the origin allowlist, which is harmless.
HAL0_PUBLIC_URL
unset
Install-time only: the reverse-proxy URL (a bare host means https://). Saved once to hal0.toml[network].public_url when that is unset, and hal0-api allows its origin from there. A value that differs from a set public_url is only added to that run’s HAL0_ALLOWED_ORIGINS.
HAL0_LAN_IPS
""
Comma/space-separated list of the box’s LAN IPv4 addresses, short-circuiting auto-detection. The installer feeds it hostname -I output — Python’s own resolver is unreliable inside some containers.
HAL0_SELF_BASE_URL
http://127.0.0.1:8080
Self-referential base URL used by the realtime backend and internal API calls.
HAL0_UI_DIST
"" (built-in dist dir)
Override the served UI static-asset directory.
HAL0_REQUIRE_AUTH
unset
Overrides [security].require_auth — wins over the persisted config value.
HAL0_TRUST_FORWARDED_FOR
unset
Overrides [security].trust_forwarded_for — wins over the persisted config value.
HAL0_LOGIN_RATELIMIT_MAX
10
Max login attempts per window before the sliding-window rate limiter cuts an IP off. Budget is deliberately loose enough for a fat-fingered operator to retry a few times a minute while capping an automated key-guessing loop at this many tries/minute/IP.
HAL0_LOGIN_RATELIMIT_WINDOW_S
60
Window (seconds) HAL0_LOGIN_RATELIMIT_MAX is measured over.
HAL0_ALLOWED_ORIGINS
"" (falls back to a built-in default set)
Comma-separated browser-Origin allowlist for state-changing requests and WebSocket upgrades. Installer-managed. hal0.toml[network] origins are added on top at runtime. A named origin (DNS name, reverse-proxy domain) must be in one of the two; only an IP-literal host passes without it.
HAL0_MCP_ALLOWED_HOSTS
"" (the box’s own names are always allowed)
Comma-separated host / host:port / host:* values added to the /mcp/* mount’s Host-header allowlist (DNS-rebinding protection). It adds to the seeded default: the box’s own loopback, detected LAN IPs, hostname, .local name and [network].public_url host, each bare and with the API port. The single value * disables that protection entirely.
HAL0_MCP_ALLOWED_ORIGINS
"" (the box’s own origins, plus any derived from HAL0_MCP_ALLOWED_HOSTS)
Comma-separated browser Origins allowed to call /mcp/*, added to the box’s own. When unset, http/https origins are derived from each host in HAL0_MCP_ALLOWED_HOSTS.
HAL0_ADMIN_KEY
minted on rotation, else read from api.env
Admin bearer key for /api/*. See hal0 auth.
HAL0_CLIENT_KEY
minted on rotation, else read from api.env
Client bearer key.
HAL0_OPENROUTER_OAUTH_ENABLED
unset (disabled)
1/true enables OpenRouter OAuth.
HAL0_COMFYUI_PUBLIC_URL
"" (falls back to http://<request-host>:8188, which answers from any machine while [network].comfyui_listen is "lan", the default, and only on the box itself under "loopback")
Public URL the dashboard uses to open ComfyUI. Set in /etc/hal0/api.env when ComfyUI sits behind a reverse proxy with its own HTTPS hostname — otherwise an HTTPS dashboard opening a bare http:// link is blocked as mixed content. GET /api/config/urls reports comfyui_public: true while it is set and comfyui_lan for the bind; the dashboard opens the editor when either is true or the page is on the box (ADR-0041).
| HAL0_COMFYUI_GATE_FAIL_OPEN | unset (ComfyUI’s GPU gate refuses a prompt while hal0 is unreachable) | 1, true, yes or on lets the hal0_gpu_gate node pass prompts through, unguarded as in v1.6.5, while hal0-api is unreachable, does not say which mode holds the GPU, or refuses the gate’s credential (401/403: the gate’s token is missing, as in a container started before the update, or no longer current). A prompt that hal0 answered and refused stays refused. Set it in /etc/hal0/api.env, restart hal0-api, then run hal0 slot restart img: hal0 writes it into the slot’s unit when it loads the slot, and the ComfyUI container reads it at start. See Image mode and the GPU gate. |
| HAL0_OPENWEBUI_PUBLIC_URL | "" (falls back to http://<host>:3001) | Public URL the dashboard advertises for OpenWebUI. Needed for a reverse-proxy deploy: OpenWebUI emits root-absolute asset URLs, so it needs its own (sub)domain, not a path prefix. |
| HAL0_OPENWEBUI_PROBE_URL | "" → http://127.0.0.1:3001/health | Loopback health-check URL for OpenWebUI, independent of the browser-facing public URL. Override for tests or a non-default deployment. |
| HAL0_OPENWEBUI_ADMIN_TOKEN | unset | Optional Open WebUI admin bearer token used only to create/update hal0’s hal0_hindsight_rag Function through individual Function endpoints. Without it, use the documented one-time manual import. |
| HAL0_OPENWEBUI_INTERNAL_URL | http://127.0.0.1:3001 | Open WebUI base URL used by the Function provisioner. Override only when the companion is not on the stock loopback port. |
| HAL0_HERMES_PUBLIC_URL | "" | Public URL the dashboard advertises for the Hermes agent dashboard. Hermes binds loopback-only (127.0.0.1:9119), so — unlike OpenWebUI — there is no host:port fallback; on a stock install the dashboard shows the health row without a link until this is set. |
| HAL0_API_URL | http://127.0.0.1:8080 (CLI client default) | Base URL the CLI uses to reach hal0-api. |
| HAL0_DASHBOARD_URL | unset → HAL0_API_URL → http://hal0.local:8080 | Base of the OAuth redirect_uri hal0-api sends a provider (<base>/api/oauth/<provider>/callback) and the dashboard link rendered into Hermes’ persona. Must match the redirect URI registered with the provider. |
Slots
Variable
Default
Controls
HAL0_SLOT_OUTPUT_SANITY
unset (gate ON)
0/false/no/off disables the output-sanity gate box-wide, so a type = "llm" slot reaches READY on the health probe alone. Per-slot equivalent: output_sanity = false in the slot TOML.
Metrics
The OBS-1 metrics core (src/hal0/metrics/config.py) is a standalone reader — it
is not part of the root Hal0Config model, so these env vars are the primary way
to tune it (alongside the optional [metrics] table in hal0.toml; env wins).
See Config schema for the [metrics] TOML table.
Variable
Default
Controls
HAL0_METRICS_ENABLED
true
Master on/off. When false, every background task (sampler, aggregator, retention) stays unconstructed — near-zero overhead, not a disabled poll loop.
HAL0_METRICS_SAMPLE_INTERVAL_S
5.0
Seconds between metric samples.
HAL0_METRICS_AGGREGATE_INTERVAL_S
3600.0
Seconds between rollup aggregation passes.
HAL0_METRICS_RETENTION_INTERVAL_S
21600.0 (6h)
Seconds between retention sweeps.
HAL0_METRICS_RETENTION_REQUEST_DAYS
7
Days of raw per-request rows kept before pruning.
HAL0_METRICS_RETENTION_SLOT_SAMPLE_DAYS
3
Days of raw slot-sample rows kept before pruning.
HAL0_METRICS_RETENTION_ROLLUP_DAYS
90
Days of aggregated rollup rows kept before pruning.
HAL0_METRICS_QUEUE_MAXSIZE
1024
Bounded depth of the async metrics-writer queue.
HAL0_METRICS_WRITE_BATCH_SIZE
64
Rows drained per BEGIN IMMEDIATE write transaction.
Hermes agent
Variable
Default
Controls
HAL0_HERMES_PYTHON
unset (uv-managed CPython 3.14)
Pins the interpreter used to build the Hermes venv. It must be the Python minor the pinned Hermes commit targets (3.14 for the current pin, which declares requires-python >=3.11,<3.15). A value that fails validation, from the environment or from /etc/hal0/hermes-python.env, is discarded with a hermes-python-discarded warning naming the value and the reason; hal0 then re-probes (a uv-managed CPython of the target minor under /var/lib/hal0/hermes/python, then a system python3.14 that links SQLite 3.51.3 or newer) and writes the interpreter it chose back to the file when the file is writable. A valid interpreter that links an SQLite older than 3.51.3 is used, with a warning. hal0’s own interpreter is never used. See Services.
HAL0_HERMES_LIVE_RESOLVE
"1"
0 opts out of live model-discovery (Hermes’ picker polling hal0-api’s /v1/models) back into single-backend pinning.
HAL0_HERMES_TERMINAL
unset (→ terminal tool OFF)
Opt the agent into a terminal tool at provisioning time. 1 enables it, 0 disables it. With it on, Hermes can run shell commands as the hal0 user, which is root-equivalent on this box. A non-interactive install never enables it; see Run agents. While the terminal is off, hal0 also writes terminal.backend: hal0-terminal-off into the Hermes config so the tool is unavailable to every agent in the gateway process; see CLI reference.
HAL0_HERMES_OFFLINE
unset
Set by hal0 agent reprovision hermes --offline. With it set, a Hermes build that is needed fails and names the step that needs the network; the live release keeps running.
HAL0_AGENTENV
/usr/lib/hal0/bin/hal0-agentenv
Privileged-seam binary hal0-api (running unprivileged) delegates to for writing root:root agent secret/env files, and for listing which secrets-vault keys are set (names only, never values). Env-overridable for tests, not a normal operator knob.
HAL0_SYSTEMCTL
/usr/lib/hal0/bin/hal0-systemctl
Privileged-seam binary hal0-api delegates to for the systemd writes/reloads it cannot do unprivileged. Env-overridable for tests, not a normal operator knob.
HAL0_INFERENCE_BASE
http://127.0.0.1:8080
Base URL the Hermes provisioner records as hal0’s inference endpoint.
HAL0_AGENT_SECRET_PATH
/var/lib/hal0/agents/secret.bin
HMAC secret that signs dashboard session cookies (hal0_session).
Identity a plugin/proxy presents as (X-hal0-Agent header to Hindsight, board/manifest-proxy routing target, the hal0-provider plugin’s own id). Written into the shim’s child env by hal0-agent; per-request overrides exist above it in some paths.
HERMES_HOME
~/.hermes
—
HERMES_SESSION_TOKEN
ignored
Removed (ADR-0046). The board token is read from /var/lib/hal0/state/agents/hermes/dashboard-session-token, which the hal0-agent shim writes; hal0 doctor warns (hermes-session-token-pin) while HERMES_SESSION_TOKEN is still set in api.env.
HERMES_DASHBOARD_SESSION_TOKEN
unset; hal0-agent@hermes supplies it
Upstream Hermes variable for the dashboard session token. Without it Hermes mints a new token at every start. hal0-agent@hermes mints one per install at /var/lib/hal0/state/agents/hermes/dashboard-session-token (0600) and exports it at every start, so a Hermes Desktop remote pairing survives restarts; hal0 agent dashboard-token hermes prints it. A systemctl edit hal0-agent@hermes drop-in that sets this variable wins over the file.
HERMES_DASHBOARD_PUBLIC_URL
unset; hal0 never sets it
Upstream Hermes variable (also dashboard.public_url in Hermes’s config). A non-loopback value turns Hermes’s dashboard login on even though the dashboard binds loopback, and the kanban board can then no longer read the session token it needs. Leave it unset.
HERMES_DASHBOARD_BASE_URL
http://127.0.0.1:9119
Upstream base URL for the Operator Board proxy (/api/board/* forwards to /api/plugins/kanban/* + its /events WS) and the manifest proxy — both loopback-only by design. The only base-URL override.
HAL0_HERMES_RUNTIME_JSON, HAL0_HERMES_HOST and HAL0_HERMES_PORT were read only by the removed agent chat proxy and have no effect; hal0 no longer writes runtime.json.
Hindsight memory
Variable
Default
Controls
HAL0_MEMORY_BASE
http://127.0.0.1:8080
Base URL the Hermes memory plugin uses to reach hal0’s memory API (precedence: ctor arg → this env var → memory.hal0.base_url in the plugin’s own config).
HAL0_MEMORY_DEFAULT_VISIBILITY
shared
Default visibility for explicit durable writes (hal0_memory_add) when the caller doesn’t pass one. Raw turn/lifecycle captures are always private regardless of this.
HAL0_MEMORY_SPOOL
unset
Directory for the memory plugin’s retry spool (writes that failed to reach Hindsight get queued here instead of dropped).
HAL0_MEMORY_REFLECT_TIMEOUT_S
900
Timeout (seconds) for the Hindsight reflect call.
HAL0_MEMORY_REPROBE_INTERVAL_S
30
Minimum interval (seconds) between the API’s re-probes of a previously-unreachable Hindsight engine.
HAL0_HINDSIGHT_URL
http://127.0.0.1:9177
Hindsight engine base URL.
HAL0_HINDSIGHT_PROBE_TIMEOUT_S
2.0
Timeout for the Hindsight reachability health check.
HINDSIGHT_API_TENANT_API_KEY
hal0-local-noauth
—
Observability (Sentry)
Optional error/trace reporting (src/hal0/observability/sentry.py). hal0 phones
home to nobody by default: Sentry stays fully inert — no SDK import, no network
egress — until a DSN is configured. Requires pip install 'hal0ai[sentry]' (or uv pip install sentry-sdk) in addition to setting the DSN.
Variable
Default
Controls
HAL0_SENTRY_DSN
unset (off)
The DSN. This is the only switch — there is no separate enable flag to drift out of sync with it. Falls back to the SDK-conventional SENTRY_DSN if unset.
HAL0_SENTRY_ENVIRONMENT
development
Sentry “environment” tag. Deliberately not production, so a copied-around DSN doesn’t silently pollute a production stream.
HAL0_SENTRY_SERVER_NAME
socket.gethostname()
Overrides the reported hostname.
HAL0_SENTRY_TRACES_SAMPLE_RATE
0.0
Float in [0, 1]. Transaction tracing is opt-in per install — sampling every transaction on a box serving long-lived streaming requests is expensive.
HAL0_SENTRY_PROFILES_SAMPLE_RATE
0.0
Float in [0, 1]. Same rationale as traces.
HAL0_SENTRY_DEBUG
unset (off)
1/true/yes prints the SDK’s own transport logging — for diagnosing “why did my event not arrive”.
Providers / external services
Variable
Default
Controls
HF_TOKEN, HUGGING_FACE_HUB_TOKEN
unset (first-set wins)
Hugging Face Hub auth, used across install, model routes, and pull jobs.
COMFYUI_BASE_URL
http://127.0.0.1:8188
—
HAL0_COMFYUI_DATA_ROOT
"" (falls back to <model store>/comfyui)
Root of ComfyUI’s data: its models/, workflows/ and custom nodes. It is the only override; the subdirectories follow it, so they cannot disagree.
HAL0_CONTAINER_RUNTIME
unset (auto-detect)
Overrides the podman/container runtime binary.
HAL0_FLM_BIN
/usr/bin/flm
Host FastFlowLM binary. hal0 uses it only for flm validate and as a last-resort flm list when the flm runner image cannot be reached. Model pulls never use it: they run inside the flm runner image.
HAL0_FLM_HOME
/var/lib/hal0
—
HAL0_FLM_USER
hal0
—
HAL0_FLM_BINARY
{native_flm_root}/bin/flm
—
HAL0_QWEN3TTS_CACHE
/var/lib/hal0/qwen3tts-cache
Host cache dir bind-mounted at /cache for the Qwen3-TTS (ROCm) provider.
HAL0_PROVIDER_BASE
http://127.0.0.1:8080/v1
Base URL the Hermes provider_hal0 plugin uses for hal0’s own OpenAI-compatible endpoint.
HAL0_AVAHI_SERVICES_DIR
"" (standard avahi services dir)
Overrides the mDNS service-file directory. Documented as a test/non-standard-init override, not a routine operator knob.
HAL0_HOST_RAM_GB
unset (auto-probed from /proc/meminfo)
Forces the detected unified-RAM figure used for bundle-tier eligibility, so an operator on a too-small box can pick a larger bundle anyway. Documented escape hatch for the (deferred, see note below) bundle-tier picker — no separate “force install” flag exists.
Overrides the bundle-manifest root the (deferred) bundle-tier picker reads. A test hook per its own docstring, not a normal operator knob.
HAL0_ALLOW_VULKAN_FALLBACK
unset
1/true/yes is an escape hatch for the ROCm device-node requirement on AMD GPU lanes (#1888). Set it only when you knowingly want a lane whose output may be invalid — there is no supported configuration where this is the right answer; it exists so a box can be inspected, not run. It cannot conjure a missing device node: it applies only to the correctness gates (missing /dev/kfd, a runner image whose digest has not passed the Vulkan gate (lanes_gated in manifest.json)), never to a missing render node.
Activity / metrics / bench
Variable
Default
Controls
HAL0_ACTIVITY_RETENTION_DAYS
falls back to [activity].retention_days
—
HAL0_BENCH_SUITE_DIR
unset
—
HAL0_BENCH_API
module-local default constant
—
HAL0_BENCH_STATE
/var/lib/hal0-bench ($HAL0_HOME/var-lib/hal0-bench under HAL0_HOME)
—
HAL0_BENCH_TELEMETRY_ROOT
module-local default root
Overrides where hal0 bench writes its telemetry tree.
Override the identity fields hal0 bench records with a run.
Misc
Variable
Default
Controls
HAL0_SETUP_TIMEOUT_SECS
300.0
Timeout for hal0 setup’s non-interactive install steps.
HAL0_FORCE_INTERACTIVE
unset
Forces hal0 setup into interactive mode even in a non-TTY.
HAL0_LOG_LEVEL
code default WARNING; the installer’s /etc/hal0/api.env pins it to info
structlog level filter, applied by the CLI’s root callback before every command runs — including hal0 serve, so it governs hal0-api.service’s own log verbosity too, since the unit’s ExecStart is hal0 serve.