Skip to content

Config schema reference

hal0 config lives under /etc/hal0/ (or $HAL0_HOME/etc/hal0/ in a dev install — see Environment variables). Config files are TOML, parsed with tomllib/validated with Pydantic models in hal0.config.schema, except hardware.json and manifest.json, which are JSON. All writes go through an atomic tempfile-write + fsync + os.replace (write_toml_atomic/write_env_atomic), so a crash mid-write can’t corrupt a config file.

File Format Purpose
/etc/hal0/hal0.toml TOML Main config — [meta], [slots], [dispatcher], [telemetry], [models], [memory], [activity], [brain_chat], [security], [realtime].
/etc/hal0/slots/<name>.toml TOML One file per slot.
/etc/hal0/providers.toml TOML Local cloud-provider API entries ([[provider]]).
/etc/hal0/upstreams.toml TOML Remote/cloud LLM upstream entries ([[upstream]]).
/etc/hal0/profiles.toml TOML Named backend-tuning profiles. Optional — seeds apply if absent.
/etc/hal0/stacks.toml TOML Named stack bundles (slot + profile + model sets). Optional.
/etc/hal0/agents/<name>.toml TOML Per-agent MCP allowlist/config.
/etc/hal0/hardware.json JSON Hardware probe snapshot, written by hal0 config hardware --refresh (formerly hal0 probe).
/etc/hal0/manifest.json JSON Release manifest — toolbox image digest pins.
/etc/hal0/api.env, /etc/hal0/openwebui.env env-file (KEY=value) systemd EnvironmentFile= — secrets/keys, not part of the Pydantic schema.

Root model Hal0Config, extra="allow".

extra="forbid".

Key Type Default Constraints
schema_version int 1 >= 1

extra="forbid".

Key Type Default Constraints
max_slots int 0 (unlimited) >= 0
port_range_start int 8081 1024-65535
port_range_end int 8099 1024-65535; must be >= port_range_start
idle_timeout_s int 300 >= 0
evict_pressure_mb int 8192 >= 0
preload_evict_enabled bool true
preload_evict_headroom_mb int 1024 >= 0
publish_host str "127.0.0.1" non-empty, no whitespace/://
network_mode str "" must be "" or "host"

extra="forbid".

Key Type Default Constraints
prefetch_timeout_s float 8.0 > 0.0
direct_read_timeout_s float 300.0 30.0-600.0
prefetch_parallel_cap int 4 >= 1

extra="forbid". Opt-in.

Key Type Default Constraints
enabled bool false
channel "stable"|"preview"|"nightly" "stable"

extra="allow".

Key Type Default Constraints
roots list[str] [models_dir()] = ["/var/lib/hal0/models"] each entry non-empty and absolute
auto_scan_on_start bool true
file_extensions list[str] [".gguf", ".safetensors"]
pull_root str "/var/lib/hal0/models" deprecated; absolute or empty
store str "" absolute if set, else empty
flm_store str "" absolute if set (NPU/FLM model store)

effective_store() resolves to store if set, else pull_root. scan_roots() returns roots plus the effective store, deduplicated. Env override: HAL0_MODEL_STORE (precedence: env → store/effective_store()models_dir() default). NPU store env override: HAL0_FLM_MODELS_DIR.

extra="allow".

Key Type Default Constraints
enabled bool true
unified_bank bool true
engine str "hindsight" one of hindsight, mem0, pgvector"cognee" is explicitly rejected (retired)
graph table see below
embedding table see below

extra="ignore".

Key Type Default Constraints
enabled bool false informational only
extraction_slot str "utility" regex ^[a-z0-9][a-z0-9_-]{0,31}$
llm_timeout_s int 300 30-3600

extra="ignore".

Key Type Default Constraints
rerank_gateway_url str "http://127.0.0.1:8080" non-empty
rerank_model str "builtin.jina-reranker-v1-tiny-en-q8" non-empty
rerank_connect_timeout_s float 1.0 0.05-10.0
rerank_read_timeout_s float 8.0 0.05-60.0

extra="forbid".

Key Type Default Constraints
enabled bool true
retention_days int 30 >= 1
max_rows int | null 50000 >= 100; null disables the cap

extra="forbid".

Key Type Default Constraints
enabled bool true
read_only bool true
model str "" empty → persona default
max_rounds int 8 1-100
completion_timeout_s float 300.0 > 0

extra="forbid".

Key Type Default Constraints
require_auth bool | null null (resolves to off) HAL0_REQUIRE_AUTH env wins if set
trust_forwarded_for bool | null null (resolves to off) HAL0_TRUST_FORWARDED_FOR env wins if set

extra="forbid".

Key Type Default Constraints
enabled bool true
sample_rate int 24000 8000-48000
default_model str ""
stt_model str ""
tts_model str "kokoro"
tts_voice str ""
vad_energy_threshold float 0.02 0.0-1.0
vad_silence_ms int 500 50-10000
vad_min_speech_ms int 200 0-10000
vad_window_ms int 20 5-100
frame_ms int 20 5-200
approval_wait_s float 20.0 > 0, <= 300.0
max_buffer_seconds float 30.0 > 0, <= 600.0

Root model SlotConfig, extra="allow". Both a legacy nested [slot] table and flat top-level scalars are accepted on load and normalized.

Key Type Default Req Constraints
id int | null null >= 1
name str required regex ^[a-z0-9][a-z0-9_-]{0,31}$
port int required 8081-8200
device str "gpu-rocm" one of gpu-rocm, gpu-vulkan, gpu-cuda, cpu, npu
gpu_index int | null null >= 0
n_gpu_layers int -1 -1 = all, 0 = CPU-only
threads int 0 >= 0; 0 = unset
binary str "" key into RUNNER_IMAGES
image_pin str | null null escape-hatch image ref override
provider str "llama-server" one of llama-server, flm, moonshine, kokoro, qwen3tts, comfyui; deprecated, legacy label only — all slots run as podman containers now
runtime "container" "container" only value; deprecated field
profile str | null null references profiles.toml
parallel int | null null >= 1; inert (sunset)
chat_template str | null null inert (sunset)
default_voice str | null null TTS slots
default_speed float | null null 0.25-4.0
default_response_format str | null null one of mp3, wav, opus, flac, pcm
workers int 1 >= 1; deprecated/inert
idle_timeout_s int 300 >= 0; 0 disables IDLE-TTL eviction for this slot only — pressure and pre-load eviction are separate paths, exempted only by pinned. Read via the raw-dict sweep path (not the typed loader), an out-of-range value there is clamped/floored at read time, not errored — the typed POST/PUT API path still hard-rejects via pydantic’s ge
pinned bool false exempts the slot from auto-eviction; blocks manual unload without force
autoload bool | null null true → the Quadlet unit gets [Install] WantedBy=hal0.target and the slot starts at boot; false → unit exists, nothing starts it. null (key absent) is the migration shim: derived from a non-empty model.default. POST /api/slots persists an explicit value (false)
priority int 50 0-100; eviction order, lowest evicted first, last_used breaks ties. 100 is not a pin — use pinned. Read via the raw-dict sweep path, an out-of-range TOML value is clamped to 0-100 at read time, not errored — the typed POST/PUT API path still hard-rejects via pydantic’s ge/le
model table [model], see below
server table [server], see below
npu table | null null [npu], see below
image (alias image_gen) table [image], see below
extra dict {} catch-all

There is no enabled field — a non-empty model.default is the activation signal. Boot start is separate and explicit (autoload); a retired lru key is still accepted but ignored, with a one-time deprecation warning. See Slot lifecycle → Activation.

extra="allow".

Key Type Default Constraints
default str "" model id from the registry
context_size int | null null >= 128. On the v1.0 branch this is a ceiling only — the model’s own context window is authoritative; effective = min(model_window, slot_value), this value never raises it.
n_gpu_layers int -1 inert (sunset)
rope_freq_base float 0.0 >= 0.0; deprecated, ignored
extra dict {}

extra="forbid".

Key Type Default Constraints
extra_args str | null null inert (sunset)
env dict[str,str] | null null keys must match ^[A-Za-z_][A-Za-z0-9_]*$; values must not contain \n/\r

extra="forbid".

Key Type Default
asr bool false
embed bool false
chat bool true

[image] (nested under a slot, alias for image_gen)

Section titled “[image] (nested under a slot, alias for image_gen)”

extra="forbid".

Key Type Default Constraints
idle_restore_minutes int 60 >= 0
default_size str "1024x1024"
default_steps int 0 >= 0

Root model ProvidersConfig, extra="allow": provider: list[ProviderEntry] (default []).

extra="forbid".

Key Type Default Req Constraints
catalog_id str required non-empty
name str "" display override
base_url str ""
auth_value_env str "" name of the env var holding the API key — the key itself is never stored in TOML
enabled bool true
models list[str] []

Root model UpstreamsConfig, extra="allow": upstream: list[UpstreamEntry] (default []); names must be unique.

extra="forbid".

Key Type Default Req Constraints
name str required non-empty
kind str "remote" slot or remote
url str required non-empty
auth_style str "bearer" bearer, anthropic, google_query, header, none
auth_header str "" required if auth_style = "header"
auth_value_env str ""
timeout_seconds float 300.0 > 0.0
slot_name str | null null required if kind = "slot"
warmup_strategy str "none" none, ondemand, always; aliases lazy → ondemand, eager → always
advertise_models bool true
enabled bool true
model_filters table | null null see below

extra="forbid".

Key Type Default Notes
models list[str] [] exact id list
include list[str] [] fnmatch globs
exclude list[str] [] fnmatch globs; always wins over include

Root model ProfilesConfig, extra="forbid": profile: dict[str, ProfileConfig] keyed by name. Seed profiles are always reapplied from code on load and overlay any on-disk copy — they can’t be edited or deleted, only cloned.

extra="forbid".

Key Type Default Constraints
flags str "" bench-tuned llama-server CLI flags
mtp bool false informational only — MTP is model-owned, not profile-owned
device_class "gpu"|"cpu"|"npu"|"img" | null null null = device-agnostic
backend "rocm"|"vulkan"|"cuda" | null null
cloned_from str | null null provenance
intent str "" dashboard card headline
quant str "" display chip only — runtime reads quant from the model, not the profile

Profiles no longer carry an image field — image resolution is slot-owned (slot.image_pin or RUNNER_IMAGES[slot.binary]).

Root model StacksConfig, extra="forbid": stack: dict[str, StackConfig] keyed by slug. STACK_SCHEMA_VERSION_CURRENT = 1.

extra="forbid".

Key Type Default
name str ""
description str ""
author str ""
icon str ""
tags list[str] []
schema_version int 1
hal0_version str ""
slots list[table] [] — see StackSlotEntry below
profiles dict[str, table] {}ProfileConfig entries
models dict[str, table] {} — see StackModelMeta below

extra="forbid".

Key Type Default Req Constraints
slot str required regex ^[a-z0-9][a-z0-9_-]{0,31}$
profile str | null null
model str | null null
device str | null null must be a valid device id if set
provider str | null null
vision bool false
mtp bool | null null
enable_thinking bool | null null
server_extra_args str | null null
capabilities list[table] [] see StackCapabilityRow below

extra="forbid".

Key Type Req Constraints
child str required e.g. embed, rerank, stt, tts, vision
device str required must be a valid device id
provider str required
model str required
enabled bool default true

extra="forbid".

Key Type Default Req
id str required, non-empty
name str ""
hf_repo str ""
hf_filename str ""
size_bytes int 0
quant str ""
capabilities list[str] []
backends list[str] []
mmproj str | null null

Root model AgentConfig, extra="allow". AGENT_CONFIG_SCHEMA_VERSION = 1.

Key Type Default Req Constraints
schema_version int 1 >= 1; future versions rejected
agent table required [agent], see below
mcp table {} [mcp], see below

extra="allow".

Key Type Default Req Constraints
name str required regex ^[a-z0-9][a-z0-9-]{0,31}$
display str ""
workspace str "" empty → canonical path

extra="allow": servers: dict[str, MCPServerConfig] (default {}).

extra="allow".

Key Type Default Constraints
url str | null null required unless builtin = true
enabled bool true
builtin bool false
auth table {} see below
tools table {} see below

extra="forbid".

Key Type Default Constraints
kind "none"|"bearer-from-env" "none"
env str | null null required if kind = "bearer-from-env"

extra="forbid".

Key Type Default
allow list[str] []
gated list[str] []
blocked list[str] []

allow, gated, and blocked must be disjoint — validation raises naming the overlapping tool names.

Root model HardwareInfo, extra="allow" (JSON, not TOML). Written by hal0 config hardware --refresh.

Key Type Default Constraints
hostname str ""
uptime_s int 0 >= 0
kernel str ""
distro str ""
cpu_model str ""
cpu_cores int 0 >= 0
cpu_threads int 0 >= 0
ram_mb int 0 >= 0
ram_available_mb int 0 >= 0
swap_mb int 0 >= 0
unified_memory_mb int 0 >= 0 — the true UMA pool; use this, not ram_mb + vram_mb, on unified-memory systems
gpus list[table] [] see GPUInfo below
gpu_group_gids dict[str,int] {} resolved render/video group ids
npu table {} see NPUInfo below
disk_free_mb int 0 >= 0
cgroup_max_mb int | null null >= 0
probed_at str "" ISO-8601
platform str "unknown" one of strix-halo, wsl2, proxmox-kvm, kvm, lxc, bare-metal-amd-gpu, bare-metal-nvidia-gpu, bare-metal-intel-igpu, bare-metal-cpu-only, unknown
extra dict {}

extra="allow".

Key Type Default
vendor str ""
index int 0
name str ""
vram_mb int 0
pci_id str ""
driver str ""
drm_path str ""
compute_capable bool false
vulkan_capable bool false

extra="allow".

Key Type Default
present bool false
vendor str ""
name str ""
driver str ""
accel_path str ""
render_path str ""
aie_columns int 0
validated bool | null null

manifest.json holds toolbox container image digest pins keyed by backend. api.env and openwebui.env are systemd EnvironmentFile=-format KEY=value files, not part of the Pydantic schema — see Environment variables for what lives in api.env (admin/client bearer keys, etc).