Highlights
- Profiles carry a runtime, and the dashboard shows it everywhere a profile
appears. A profile can pin arunner; profile cards carry a runtime badge,
the import preview shows the envelope's runtime before commit, and the slot
Edit drawer's apply preview enumerates the runtime, lane, flags and restart a
profile change would actually cause before you save it. - The seed profile catalog is pruned from 18 to a 10-profile minimal core,
and the eight demoted tunes ship as portable.hal0profile.jsonaddon
envelopes undercommunity/addons/that Profiles → Import installs from a
paste or an upload. - The model and slot drawers were reworked end to end — task-grouped slot
fields, rich selects, GTT hints, inline model pull, tune pills, a facts band,
and an overrides ledger naming every field that diverges from the stamped
profile. - Host-truth GTT feasibility.
GET /api/hardwarecarries live
gtt_used_mb/gtt_free_mbfrom the amdgpu sysfs counters, and the slot load
path emits an advisoryslot.gtt_feasibilitywarning — never a block — when a
model's weights exceed the free GTT pool. - Arch-aware model↔runner fit-check. GGUF
general.architectureis detected
and persisted at registration, and binding a model whose arch the runner
denylists now WARNS at assignment, naming the catalogued image that can serve
it, instead of leaving the operator at a silent crash-loop. - The dashboard surfaces the decisive crash line when a slot container dies
during model load, stamped on slot state and carried by the crash-breaker
chip's tooltip. - The Proxmox VE quick-start script builds a privileged GPU-passthrough LXC by
default, so a fresh container reaches the GPU without a manual pass.
Fixed
- Editing a profile whose stored runtime key has left this box's runner
registry (renamed, dropped by a build, or a downgrade) no longer 422s with
profiles.unknown_runner. The drawer re-sends the storedrunneron every
save, so screening an unchanged value rejected a write that changes nothing
and made the profile un-editable in every field.PUT /api/profiles/{name}
now screensrunneronly when it actually changes — the same grandfathering
the profile's stored flags get (#1411); creates stay strict (#2183). - Clearing a profile's Runtime back to "Auto" in the profiles drawer now
actually clears it.PUT /api/profiles/{name}readsrunner: nullas
"leave unchanged" (as it does for every other field), so the Auto option
silently no-op'd and the runtime badge came back after save. The empty
string is now the explicit clear sentinel on that field — never screened,
so it is also the way off a stored runtime this box no longer has (#2186). hal0 updatenow reads the installed version from the active release's
current/VERSIONfile instead of the wheel metadata. A nightly wheel
deliberately carries only the BASE version in pip metadata (PEP 440 has no
shape forv<base>-nightly.<stamp>), so a box already on the newest
nightly compared1.1.0 < 1.1.0-nightly.<stamp>and looped "update
available" forever, re-staging the same release on every run.
Added
-
The Proxmox VE quick-start script (
scripts/proxmox-ve/hal0.sh) now builds
the production hal0 container shape instead of a vanilla CPU-only one: a
privileged Ubuntu 26.04 LXC withnesting/keyctl/fuse/mknod, every
GPU/NPU device node the host exposes forwarded asdevN, the matching
lxc.cgroup2.devices.allowrules,lxc.prlimit.memlock: unlimited, and
lxc.apparmor.profile: unconfined. It verifies the forwarded nodes are
actually visible inside the container before installing anything, so a
broken passthrough fails loudly instead of quietly seeding CPU slots. New
env knobs:IP_CIDR/GATEWAY,NAMESERVER,SEARCHDOMAIN,TIMEZONE,
TAGS,ONBOOT,STARTUP,MOUNTS,FEATURES,GPU_PASSTHROUGH,
RUN_BOOTSTRAP.UNPRIVILEGED=1 GPU_PASSTHROUGH=0 OS_TYPE=debian OS_VERSION=13reproduces the previous behaviour. Guest prerequisites are
no longer pre-staged by the script — bootstrap and install.sh install
their own (curl excepted: it is what fetches the bootstrap). -
hal0_lxc_kindininstaller/lib/preflight.shclassifies the platform as
none/lxc-privileged/lxc-unprivilegedfrom/proc/self/uid_map,
and the installer's/dev/kfdgid-repair fallback and container-runtime
gate now narrate the remedy that is actually possible on the container at
hand instead of always describing the unprivileged one. -
The dashboard now surfaces the decisive crash line when a slot container
dies during model load. On a load failure the slot manager tails the
unit's journal, extracts the one line that names the fault (e.g.
llama_model_load: error loading model: unknown model architecture,
unable to allocate ROCm0 buffer, or the hal0-runner death summary from
#2037/#2126) and stamps it on the slot state (metadata.last_crash_line);
the crash-breaker chip's tooltip carries it, so "trial pending" finally
says why instead of pointing atjournalctl -u hal0-slot@<name>. -
Runner-image catalogue rows now carry
runtime_familyand
supported_backendsruntime metadata merged from the
hal0.runner-images.v1manifest, and the slot drawer's
"catalogued · downloaded" pin lane gates each row on its declared
runtime family instead of assuming every catalogue row is a
llama-server fork (#2174). -
Host-truth GTT feasibility signal.
GET /api/hardwarenow carries live
gtt_used_mb/gtt_free_mbfrom the amdgpumem_info_gtt_*sysfs counters
(host truth even inside an LXC container, where the cgroup-shaped meminfo
can show tens of GiB "available" while a GTT weight allocation fails), the
slot load path emits an advisoryslot.gtt_feasibilitywarning — never a
block — when a model's weights exceed the free GTT pool, and the dashboard's
Detected-hardware panel shows the live free-GTT figure. -
Arch-aware model↔runner fit-check (#2118 follow-up). GGUF
general.architectureis now detected and persisted at model
registration (scan commit, add-from-path, pull, curated auto-scan), and
runner registry entries can denylist archs their llama.cpp build rejects
(Runner.unsupported_archs—qwen4expon the default ROCmFPX image).
Binding such a model to a slot now WARNS at assignment — API
model_fit_warningon slot create/config/swap, plus a notice by the slot
drawer's Model select — naming the catalogued image that can serve the
arch, instead of the operator meeting a silent crash-loop at load. An
image_pindisarms the check (the pin is the documented escape hatch);
nothing is ever blocked and no image is auto-switched. -
Seed profile catalog pruned to a 10-profile minimal core. The seeded
catalog carried 18 profiles — one per runtime family, the generic
llama-server workloads, and eight WORKLOAD VARIANTS of those generics
(chat-long-context,dense,moe,thinking,coding,
chadrock-dense,chadrock-moe,promptforge) — and since a seed is
virtual and can never be edited or deleted, shipping eighteen of them made
the Profiles page mostly un-actionable furniture. The eight move
byte-identically intolegacy_seed_profiles.toml
(LEGACY_SEED_PROFILES) and stop being seeds. An install upgrading past
the prune gets them injected once into its ownprofiles.tomlas
ordinary custom entries (ProfilesConfig.legacy_seeds_migrated), so
nothing an operator's slots or models already point at disappears — the
entries just become editable and deletable like any custom profile,
which a seed never could be. A FRESH install never sees them in its
catalog, but the curated slots and seed stacks it ships still name three;
each such name is adopted intoprofiles.tomlon first reference,
independently of the others, and stays deleted once deleted. -
Pruned seeds ship as portable addon envelopes. The eight demoted
tunes are published as.hal0profile.jsonenvelopes under
community/addons/(scripts/export_addons.py, byte-for-byte
reproducible,--checkfor CI drift), alongside a versioned
index.jsoncatalog (kind: hal0.addon-index) a site can serve
statically so a client can see title, description, runtime family and
checksum before fetching an envelope. The dashboard's Profiles → Import
installs one with a paste or an upload.min_hal0_versionis 1.2.0 —
1.1.0 shipped portable profiles without the import-side unknown-runner
handling the addon contract relies on. See
docs/reference/profile-addons.mdx. -
Profiles carry a runtime; the dashboard shows it everywhere a profile
appears. A profile can now optionally pin arunner(aRUNNER_IMAGES
registry key, validated on write, stripped to Auto on import if the box
doesn't have it) — the profiles form drawer gets a full Runtime select
with a consequence hint, profile cards carry a runtime badge/state
chip/blurb, the import preview shows the envelope's runtime and runtime
family before commit, and the slot Edit drawer's apply preview enumerates
the runtime, lane, flags and restart a profile change would actually
cause before you save it.
Changed
- The ComfyUI runner family now defaults to hal0's own published image
ghcr.io/hal0ai/hal0-comfyui(digest-pinned inmanifest.json) instead of
the third-partydocker.io/kyuz0/amd-strix-halo-comfyui. The image mirrors
the kyuz0 layout by construction, so existing img slots need no
reconfiguration;--purgeuninstall still removes the old kyuz0 image on
upgraded boxes (#2171). - The model and slot drawers were reworked end to end. The slot drawer
regroups its fields by task, uses rich selects for model/profile/runtime,
carries GTT hints and an inline pull for a model it does not have yet, and
shows one Advanced image row (a single ref with a live-state chip) instead
of the previous overflow. The model drawer gains header meta, a
seed-from-profile button, tune pills, a facts band, an explicit engine
field, and an overrides ledger that names every field diverging from the
stamped profile (#2206, #2208, #2210, #2215). - Dashboard: the model Capabilities/Labels editors are retired (#2193). Capabilities
display read-only in the model drawer; the Add-from-HF flow now offers a repo's
mmproj projector directly and derives the vision label from that choice; the
add-by-path flow always auto-detects labels./api/modelscontracts are unchanged.
Migrations
profiles.tomlgains two bookkeeping keys and is not readable by
1.1.0. The seed prune records its one-time work in the file itself:
legacy_seeds_migrated(the bulk demotion has run) and
legacy_seeds_adopted(which demoted names this install has already
materialized or deleted, so a delete sticks and a fresh box can still adopt
each one on first reference). Both are written automatically on the first
load or profile save after upgrading — nothing to run by hand. But
ProfilesConfigisextra = "forbid"on 1.1.0 and earlier, so rolling
back to 1.1.0 after the upgrade fails to parse the migrated
profiles.tomland the profile catalog will not load on the old build.
Back up/etc/hal0/profiles.tomlbefore upgrading if you may downgrade;
restoring that copy (or deleting the two keys) makes the file readable by
1.1.0 again.