Every TOML file under /etc/hal0/ is validated against Pydantic v2 models at
load time. A typo like device = "gpu-rcom" raises a ValidationError with
the offending field path rather than failing later at dispatch.
When HAL0_HOME is unset, hal0 uses the standard layout. Setting
HAL0_HOME=/some/path re-roots everything under that prefix (used for dev
installs and tests).
File
FHS path
Purpose
hal0.toml
/etc/hal0/hal0.toml
Top-level config.
slots/<name>.toml
/etc/hal0/slots/<name>.toml
One file per slot.
profiles.toml
/etc/hal0/profiles.toml
Profile catalog (optional; seeds used if absent).
providers.toml
/etc/hal0/providers.toml
External LLM providers.
upstreams.toml
/etc/hal0/upstreams.toml
Slot + remote upstream catalog.
capabilities.toml
/etc/hal0/capabilities.toml
Capability-slot selections.
agents/<name>.toml
/etc/hal0/agents/<name>.toml
One file per bundled/user agent.
stacks.toml
/etc/hal0/stacks.toml
Named slot+profile+model bundles (optional; see Stacks, below).
hardware.json
/etc/hal0/hardware.json
Cached hardware probe (written by hal0 probe).
manifest.json
/etc/hal0/manifest.json
Release manifest (pinned toolbox image digests).
State (registry, models, slot state, memory banks) lives under
/var/lib/hal0/. Config and state are both preserved across updates.
First port in the auto-allocation pool for new slots.
port_range_end
int
8099
Last port in the auto-allocation pool (inclusive); must be ≥ start. The pool stops below 8188 so a new slot never claims ComfyUI’s port — this only bounds auto-allocation, not an individual slot’s port field, which validates up to 8200 (see [slot].port below).
idle_timeout_s
int (≥0)
300
Default idle-eviction TTL; 0 disables. Per-slot value overrides.
evict_pressure_mb
int (≥0)
8192
Host free-RAM floor (MiB) for pressure-driven LRU eviction. When host MemAvailable drops below this, idle LRU-eligible slots are evicted in least-recently-used order until free RAM recovers; 0 disables pressure eviction.
publish_host
str
"127.0.0.1"
Address slot containers publish their port on. 0.0.0.0 exposes every slot’s raw port directly on the LAN, bypassing the hal0-api/reverse-proxy front door; a specific interface IP binds just that address. Applies on the slot’s next (re)start; host-networked slots ignore it.
Single source of truth for where models are read/written. When set, the pull engine writes here and slot containers bind-mount it. HAL0_MODEL_STORE overrides.
Hindsight embeds server-side with its own bundled model, so this table no
longer pins an embedding model — it only tunes the second-pass reranker
hal0 runs over cross-bank recall results.
Key
Type
Default
Notes
rerank_gateway_url
str
"http://127.0.0.1:8080"
Base URL of the OpenAI-compatible gateway the reranker POSTs /v1/rerankings to. Defaults to hal0-api itself.
rerank_model
str
"builtin.jina-reranker-v1-tiny-en-q8"
Rerank model id sent to the gateway; must resolve to a model the rerank slot serves.
rerank_connect_timeout_s
float (0.05–10.0)
1.0
TCP connect timeout for the rerank call. Kept short so a wedged rerank slot can’t stall memory search.
rerank_read_timeout_s
float (0.05–60.0)
8.0
Read budget for the rerank slot. Failures on either timeout fall through to fused vector ordering, never blocking recall.
Off by default. On the Hindsight engine this is a reporting gate — Hindsight builds its graph natively; vector recall works either way.
extraction_slot
str
"utility"
The local type=llm slot Hindsight uses for graph extraction. Propagated to hindsight-api as HINDSIGHT_API_LLM_MODEL=hal0/<slot> via a systemd drop-in. Validated against the live enabled-llm-slot set at set-time. Slot-name grammar (lowercase alnum/-/_, ≤32, leading alphanumeric).
llm_timeout_s
int (30–3600)
300
Timeout for the Hindsight daemon’s LLM calls — extraction, consolidation, and reflect. Covers cold slot starts and long reflects. Propagated to hindsight-api as HINDSIGHT_API_LLM_TIMEOUT via the same systemd drop-in as extraction_slot; the daemon restarts to pick it up.
The dashboard’s Settings → Advanced panel is schema-driven, not hand-copied:
it reads GET /api/settings/schema (Pydantic field types/bounds/descriptions)
and GET /api/settings/apply-plan (per-key apply_class: live / ⟳ restart a
service / ⚠ manual restart) and renders a control per key, so the dashboard
cannot drift out of sync with this page. It groups every key above — except
[meta] and [models], which have their own dedicated General/Storage pages —
into four panels:
Memory — [memory] engine + reranker (engine, the four
[memory.embedding] keys); a separate Memory graph extraction panel
right below it drives [memory.graph] through its own
GET/PUT /api/memory/graph endpoints instead of the generic settings PUT.
A dirty manual-restart key gates Save behind a confirm dialog; Save
batches every dirty key into one deep-merged PUT /api/settings. A
Restart hal0-api control sits below the groups for service-restart/
manual-restart keys that need it.
Pins the slot to one GPU on multi-GPU hosts (HIP_VISIBLE_DEVICES/ROCR_VISIBLE_DEVICES on ROCm, GGML_VK_VISIBLE_DEVICES on Vulkan, CDI device mapping on CUDA). null = all GPUs. No dashboard control yet — TOML/CLI only.
provider
str
llama-server
Engine label (round-trips for compatibility/UI).
profile
str | null
null
Profile name from profiles.toml (image + tuned flags).
role
str | null
null
Role hint: primary, utility, npu. Derived from name when unset.
enabled
bool
true
Start this slot on hal0 startup.
enable_thinking
bool | null
null
Per-slot reasoning default; overridable per request.
mtp
bool | null
null
Tri-state multi-token-prediction override: true forces on, false forces off, null (Auto) enables MTP only when the profile opts in (profile.mtp) and the model itself ships MTP heads (registry mtp tag, or a delimited mtp marker in the model id/filename, e.g. -MTP-/_mtp/.MTP).
chat_template
str | null
null
Chat-template id override (auto/null = GGUF-embedded).
vision
bool
true
Loads the model’s mmproj sidecar (--mmproj) when present, so the slot accepts images. Set false to boot text-only and free the ~0.9 GB resident projector. No effect when the model has no sidecar.
workers
int (≥1)
1
Parallel request workers.
idle_timeout_s
int (≥0)
300
Idle TTL for this slot; 0 disables.
default_voice
str | null
null
TTS slots only. Voice id injected into /v1/audio/speech when the request omits voice. null = engine default. Set from Settings → Voice.
default_speed
float | null (0.25–4.0)
null
TTS slots only. Playback speed injected when the request omits speed. Engines clamp further (Kokoro: 0.5–2.0).
default_response_format
str | null
null
TTS slots only. One of mp3 | wav | opus | flac | pcm, injected when the request omits response_format. null = engine default (mp3).
When unset, hal0 derives the context window from the model’s native size (capped) or a safe floor; never silently falls back to 4096.
n_gpu_layers
int
-1
Layers offloaded to GPU; -1 = all.
rope_freq_base
float (≥0)
0.0
Deprecated (accepted, ignored) — parsed and round-tripped but never emitted by the container launch path. Set --rope-freq-base <n> via [server].extra_args instead.
Freeform llama-server CLI passthrough; tokenised and merged with the profile/model flags — this segment wins last, so it overrides the same flag set anywhere else in the chain.
env
dict[str, str] | null
null
Extra environment variables injected into the slot container (--env K=V), e.g. HSA_OVERRIDE_GFX_VERSION. Keys must match [A-Za-z_][A-Za-z0-9_]*; values must not contain newlines. Explicit env keys win over any GPU-visibility vars gpu_index derives.
Idle minutes before the GPU arbiter restores stopped LLM GPU slots; 0 = never. Hot-reloadable — unlike most slot-TOML values, a change here takes effect without restarting the slot.
A profile is a reusable backend template: a container image plus a bench-tuned
flag bundle. Slots reference a profile by name; the profile supplies the image
and flags, the slot supplies the model, context size, and port.
Key
Type
Notes
image
str
Container image ref (required).
flags
str
Bench-tuned llama-server flags (no model/port/ctx args).
mtp
bool
Profile opt-in: when true, a slot in Auto mode enables MTP for models that are actually MTP-eligible. It does not unconditionally append the speculation bundle to every model.
device_class
gpu | cpu | npu | img
Device class the profile targets.
backend
rocm | vulkan | cuda | null
GPU runtime (authoritative ROCm/Vulkan/CUDA choice); null for non-GPU profiles.
cloned_from
str | null
Provenance: the profile this one was cloned from (set by the dashboard’s Clone / “Edit a copy” flow). Informational only.
intent
str
Human label shown in the dashboard.
quant
str
Weight quant format shown as a chip — informational only, never read by the runtime.
Current seed profiles (12): rocm, rocm-dnse, rocm-moe, vulkan (GPU/ROCm
and Vulkan chat profiles — rocm is the gpu-rocm default, MoE/agents, FP4, no
MTP; rocm-dnse/rocm-moe opt into MTP), cuda (experimental NVIDIA, upstream
llama.cpp CUDA image), embed and rerank (dedicated GPU lanes — these must
run as separate slots, since combining --embedding and --reranking on one
llama-server instance yields all-zero scores), flm (NPU), tts (CPU /
Kokoro), tts-qwen3 (GPU / Qwen3-TTS, multilingual), cpu-llm (CPU-only
chat, no MTP), comfyui (image generation).
A stack is a named, portable bundle of slot assignments — one
[stack.<slug>] entry holding a list of slot rows (each with slot, model,
profile, device, role, vision, the mtp tri-state, server_extra_args,
and optional embed/rerank/stt/tts/vision capability child rows) plus the
profile and model metadata those rows reference, embedded so the stack is
self-contained. stacks.toml is optional; three seed stacks (saber, forge,
pi) ship built in and are immutable like seed profiles — clone under a new
name to customize one. Applying a stack writes each row through the same
guarded slot-config pipeline as a manual edit (device/profile coherence, NPU
exclusivity, default-uniqueness), then converges the runtime: it loads or
swaps every slot the stack names and unloads any dispatchable slot the
stack doesn’t mention — applying a stack is a declarative replace, not a
merge. Stacks can be exported/imported as a portable .hal0stack.json
envelope, and POST /api/stacks/snapshot can capture the live running
configuration into a new draft stack.