Skip to content

Configuration

hal0’s runtime configuration is split across a few TOML files under /etc/hal0/. Every write is atomic; every field is schema-validated; a bad edit can never produce a half-written file.

PathWhat it holds
/etc/hal0/hal0.tomlThe main daemon configuration file.
/etc/hal0/capabilities.tomlUser-facing capability config (embed / voice / img / NPU rollups).
/etc/hal0/slots/<name>.tomlPer-slot lower-layer config (provider, model, port, options).
/etc/hal0/slots/<name>.envPer-slot env file consumed by the systemd unit.
/etc/hal0/hardware.jsonOutput of the hardware probe (see below).
/etc/hal0/openwebui.envOpenWebUI env, written by the installer.
/etc/hal0/proxmox.jsonOptional PVE API token + endpoint for the host-pressure segment in the memory bar. 0600, redacted on read.
/etc/hal0/api.envAPI env (auth toggles, secrets). Written by the installer.

/etc/hal0/ is preserved across hal0 update. The installer never overwrites a user-modified config file; it skips with a warning if the file already exists.

If hal0 is running in an LXC on a Proxmox node, dropping a read-only PVEAuditor token + endpoint into /etc/hal0/proxmox.json (Settings → Proxmox integration writes this for you) lights up the “Proxmox host” segment in the dashboard’s unified-memory bar. Without it, the panel stays quiet and the memory bar shows the LXC’s cgroup slice only. The file is mode 0600; tokens are redacted in logs and on the config-read API.

capabilities.toml is the user-facing config layer — embed / voice / img cards and the NPU backend rollup. It sits on top of the per-slot slots/*.toml files; the capability orchestrator reconciles drift between the two on every apply.

Three supported workflows:

  • Dashboard. The Settings view exposes most fields with a form for each section. Saves go through the atomic-write path.
  • CLI.
    Terminal window
    hal0 config edit # opens $EDITOR
    hal0 config validate # schema check without writing
    hal0 config show # print merged view
    hal0 config migrate # apply schema migrations to an older config
  • Direct edit. Open the file in any editor. On the next hal0-api restart (or a hal0 config validate run), the schema enforces shape — a typo fails loudly, not silently.

Every config write goes through:

NamedTemporaryFile(delete=False) → write → fsync → os.replace()

If the process crashes mid-write, the prior file is fully intact; os.replace() is atomic on POSIX filesystems. There’s no window in which /etc/hal0/hal0.toml is half-written.

The same pattern applies to per-slot env files; a failed slot config update leaves the previous env in place.

The config schema is Pydantic-defined in src/hal0/config/schema.py and exported as JSON Schema for reference. Every field has:

  • A type (Python type or JSON Schema primitive).
  • A default value where reasonable.
  • A constraint set (regex for slot names, range bounds for ports).
  • Inline documentation that the dashboard surfaces as help text.

A failed validation surfaces as a structured config.invalid error with details.path pointing at the offending field.

See Config schema reference for the full field list.

/etc/hal0/hardware.json is probe output, not config. It’s regenerated by hal0 probe and consumed by the slot loader to pick sensible defaults and surface VRAM/RAM fit warnings inline.

Re-run the probe whenever the hardware changes (BIOS UMA carveout, GPU swap, RAM upgrade, or changing the LXC’s dev0dev3 passthrough set):

Terminal window
hal0 probe

The output is structured: cpu_*, mem_*, gpu_*, npu_*, plus provider availability flags.

Gotcha: [model] default in slot TOMLs is a seed

Section titled “Gotcha: [model] default in slot TOMLs is a seed”

In each /etc/hal0/slots/<name>.toml, the [model] default field is the install-time seed — the model the installer picks the first time. It is NOT a live default. Once a slot is provisioned, model swaps write the active selection to env + a runtime override; the TOML file deliberately stays stale. If you edit default on a running box and nothing changes, this is why. Use hal0 slot swap <name> --model <ref> (or the dashboard’s model picker) to swap.

  • Hot-reload of selected fields (changing an idle timeout without restarting the slot).
  • Encrypted secrets storage for upstream API keys.
  • Multi-file config (drop-in fragments under /etc/hal0/conf.d/).