Repository navigation
hal0 v1.6.5
Highlights
- FLM on the NPU runs on FastFlowLM 1.0.10, inside its runner image.
The installer's host.debmoves to 1.0.10, andflm pullandflm list
now run in the pinnedflmrunner image through a newhal0-flmctl
seam, so a box without the host.debcan pull and list NPU models. An
NPU slot loaded with a model override no longer downloads a different
model, and the NPU pickers list every installed FLM chat model once,
with itsflmchip. - Tighter privilege boundaries around the
hal0account. Root
wrappers no longer import Python from the caller's working directory,
root writes under/var/lib/hal0and/etc/hal0no longer follow a
swapped-in symlink, the Hermes repair child gets an allow-listed
environment, and Settings → Secrets refuses loader, TLS, OpenSSL, proxy,
XDG and Hugging Face endpoint variables.hal0 doctor allflags any such
entry already inapi.env. - The upstream Hermes gateway unit move holds up in partial and masked
states. A move that fails or is interrupted keeps exactly one gateway,
a runtime or persistent mask is honoured, and the remedy it prints works. - Four CLI spellings are gone (see Breaking).
hal0 agent upgrade hermes --to,hal0 update owui,hal0 agent bootstrap hermes, and
hal0 uninstall --keep-data/--clean-slatenow have one name each, and a
Hermes--targetpersists until--clear-override. - Dashboard navigation. The sidebar lists every tab of every page, and
each tab has its own address you can link to.
Breaking
-
hal0 agent upgrade hermes --to <sha>is gone: usehal0 update component hermes --target <sha>. There is no alias.hal0 agent upgrade hermesnow exits with a message naming the new command, andhal0 agent upgradetakes onlypi. The--targetcheck is the one--tohad: a full 40-hex commit listed inVETTED_HERMES_REFS, and a commit older than the live release is refused as a downgrade. The hidden--refflag ofhal0 agent bootstrap hermes, which only the old command used to hand a ref to the privilege-dropped child, is removed with it. (#2865) -
hal0 update owuiis gone: usehal0 update component openwebui. There is no alias.--target <digest>,--clear-override,--checkand--yeswork onhal0 update component <id>exactly as they did onhal0 update owui; the OpenWebUI override marker and the/api/updates/components/openwebui/convergebody are unchanged. Every other command's remedy text that namedhal0 update owuinow names the new spelling.hal0 update owuiexits with the CLI's unknown-command error. (#2868) -
hal0 agent bootstrap hermesis gone: usehal0 agent reprovision hermes. The two were the same command, and there is no alias.reprovisiontakes--repair,--verboseand now--offline(a build that is needed fails and names the step that needs the network; a running host keeps its live release). The--dry-runand--skip-phaseflags that only printed "not supported" are removed with the command:reprovisionrejects them as unknown options.hal0 agent bootstrapexits with the CLI's unknown-command error. The dashboard's Hermes repair (POST /api/agents/hermes/repair) now runshal0 agent reprovision hermes --repair --verbose, and every doctor row, remedy string, installer wrapper message and agent-facing template that named the old verb names the new one. (#2863) -
hal0 uninstall --keep-dataand--clean-slateare gone.--keep-datadid nothing: the conservative default it named was already the default.--clean-slatewas a second spelling of--purge. There are no aliases, sohal0 uninstallnow has the conservative default,--purge,--forceand--dev, and the removed flags fail with Typer's "No such option" (exit 2).installer/uninstall.shfollows: it no longer accepts either flag, and any flag it does not know now exits 2 instead of printing a warning and carrying on, so an old runbook that passed--clean-slatecannot quietly run the conservative teardown. Scripts that passed--keep-datashould drop it; scripts that passed--clean-slateshould pass--purge. (#2866)
Changed
-
The NPU slot drawer's chat-model picker matches the one on the slot card. It was a plain dropdown that appended "⬇ download" to each name. It now uses the same two-line picker as the card. Each row shows the tag and, for chat models that also transcribe, the
asrtag on its first line, then the purpleflmchip on the second. A tag that is not on disk yet adds a⬇ downloadchip and is listed after the installed ones. Picking one still downloads it first. -
The NPU occupancy card's slot cards have the same model dropdown as the inference cards. The model name was a static label. Each FLM slot card now carries the dashboard's rich model picker (quant, runtime and modality chips, with the runtime chip in the NPU purple). It lists only FLM models of the card's role: chat for the chat anchor, speech-to-text for STT, embedding for Embed. Picking a chat model writes the same config the slot editor writes (
[npu]toggles plus[model].defaulton the anchor) and cold-restarts it. The STT and Embed pickers are read-only, because FastFlowLM loads one bundled model per role and has nothing to switch. -
The dashboard sidebar has a sub-link for every tab on a page. Slots adds Inference and Image Gen beside Endpoints, Runner Images and Stacks. Models lists Inference Models, Image / ComfyUI, Upstream Models and Profiles. Benchmarks lists Roster, Runs, Evals and Run Queue. Agents adds Overview, and Settings lists its sections from the same list as the in-page settings nav. Every one of these tabs now has its own address (
#slots/image,#models/upstream,#benchmarks/evalsand so on), so the sidebar highlight follows the tab you pick on the page, and a tab can be bookmarked or linked. Bare#slots,#models,#benchmarks,#agentand#settingsstill open the first tab. -
The installer installs FastFlowLM 1.0.10 on NPU hosts, up from 0.9.44. The pinned host
.debnow comes fromROCm/FastFlowLM, where the project moved, with new SHA-256 pins for each distro build. FastFlowLM 1.0 drops ffmpeg and fftw and needslibxrt22.25 or newer. The matchingflmrunner image ships separately as a runner pin. -
A Hermes
--targetnow persists until--clear-override, the way the OpenWebUI digest override does.--tobuilt a vetted commit that the nexthal0 updatereplaced with the pin.hal0 update component hermes --target <sha>records the commit in/var/lib/hal0/state/hermes.pin-overrideonce its release is live, and every laterhal0 updatekeeps that release.hal0 update statusshows the row asoverridewith the override commit beside the pin, on every run, so a forgotten override stays visible; the converge counts a live release that matches the override as converged. When a later hal0 release no longer lists the override commit inVETTED_HERMES_REFS,hal0 updatewarns, refuses to keep or rebuild it, and nameshal0 update component hermes --clear-override; it never builds an unvetted commit.hal0 update component <id>also takes--yes.--targetwith the release pin's own commit counts as--clear-override;--clear-overrideis refused when the pin is older than the live release and removes the marker only after the converge back to the pin succeeds;hal0 agent reprovision hermes --repair, the dashboard Repair and the MCP-client auto-repair inhal0 updaterebuild the override commit instead of the pin (and refuse an override this release no longer vets); and the Agents card does not show an update for a box held at an override.hal0 agent uninstall hermesremoves the override marker (a fullhal0 uninstall --purgeremoves it with/var/lib/hal0; a conservative uninstall keeps it with the rest of the state), so a fresh install never rebuilds an old override. (#2865)
Fixed
-
The NPU model picker lists every installed FLM chat model, once, with its
flmchip. FLM reports gemma4-it as chat plus speech-to-text, and/api/modelstyped its registry rowtranscription, so the NPU chat picker (which lists only chat models) dropped gemma4-it:e2b and showed the current gemma4-it:e4b as a bare row with no chips. FLM rows that can chat are now typed chat, the rule the rows built from FLM's own model list already followed. Installed FLM models with no registry row used the old<tag>-FLMid and carried no engine field, so they showed in a second naming style without theflmchip and read as duplicates. They now use FLM's native tag (qwen3:4b) like pulled models do, keep the old id as an alias for slots still bound to it, and carryprovider_effective: flm. Assigning a native tag to a slot passes the installed-model check, which accepted only the-FLMform before. -
Loading an NPU slot with a model override no longer downloads a different model and sticks in
warming. The one-shot inference check that promotes an FLM slot to ready read the model from[model].default, which an override load (hal0 slot load flm -m <model>, orPOST /api/slots/{name}/loadwith amodel_id, as the dashboard pickers send) only writes after the check. With an empty or different default, the check fell back to the first model in FastFlowLM's/v1/models, and FastFlowLM 1.0.10, which lists models it has not installed, pulled that model (5.4 GBdeepseek-r1:8bin the report) and timed out. The check now probes the model the load started, and fails withexpected_model_not_servedinstead of naming any other model. The embeddings check on chat-off NPU slots names the bundledembed-gemma:300minstead of the first chat tag.hal0 slot loadon a slot with no model now says "Not loaded" with the reason and exits 1, instead of "Loaded ... state=offline". (#3023) -
The move off upstream's suffixed
hermes-gateway-<hash>.servicenow holds up in mixed and interrupted states. After a move killed half way (a disabled, stoppedhermes-gateway.servicebeside the enabled suffixed unit),hal0 agent install hermesand the installer read the canonical unit's "disabled" and stopped the gateway again; they now read the operator's on/off state by the same rule the move uses. A maskedhermes-gateway.serviceno longer leaves a running suffixed gateway connected: both the command and the installer move it off, keeping the mask. A failed start during a resumed move now disables the canonical unit it had enabled, so both units are never left enabled. A timed-out or failedsystemctl is-activeprobe aborts the move before any change instead of reading as "stopped". A suffixed unit with a secondUser=orHERMES_HOME=assignment is no longer treated as hal0's, a canonical unit linked in withsystemctl linkis no longer treated as a mask, and a runtime mask is honoured. When only the removal of the old suffixed file fails, the command says the gateway moved instead of saying it is not enabled. (#2950) -
A runtime mask on
hermes-gateway.serviceis honoured on a box that has only the suffixed unit. systemd reads/etc/systemd/systembefore/run/systemd/system, sosystemctl mask --runtime hermes-gateway.serviceonly takes effect while there is no/etc/systemd/system/hermes-gateway.service, which is exactly the state of a box a 1.6.0 nightly installed. There,hal0 agent install hermesand the installer used to write the/etcunit, which hid the mask, then enabled and started the gateway. They now read the mask, stop and remove the suffixed unit, and write no canonical unit, so the gateway stays off. The runtime mask is gone after a reboot, and with it the off switch; with no unit file left, the gateway stays off untilsudo hal0 agent install hermeslays the unit down and turns it on. (#3024) -
The left-off note for a masked gateway names a command that works. After the move past a mask,
hal0 agent install hermesand the installer said to runsystemctl unmask hermes-gateway.service && systemctl enable --now hermes-gateway.service. The enable failed with "Unit file hermes-gateway.service does not exist": a mask leaves no gateway unit file. The note now sayssystemctl unmask hermes-gateway.service && sudo hal0 agent install hermes(unmask --runtimefor a runtime mask), which lays the unit down and turns it on. (#3025) -
The removed suffixed gateway unit no longer shows up in
systemctl --failed. After a successful move,hermes-gateway-<hash>.servicestayed listed asnot-found faileduntilsystemctl reset-failed. The move now runssystemctl reset-failedon each suffixed unit it removed; a failure there is ignored. -
The gateway move reports its leftovers and rollback failures correctly. When only the removal of the old suffixed unit file fails, the installer now says the gateway moved and that a re-run removes the file, instead of "hermes-gateway unit not installed"; this holds even when the
daemon-reloadafter it fails too, andhal0 agent install hermesstill turns linger on forhal0. A rollback that cannot disable or stop the canonical unit it had turned on now says so instead of quietly leaving both units enabled, and a rollback after a runtime-only enable now usesdisable --runtime. (#2950) -
A queued benchmark run that leaves a slot down no longer reads as a clean success. The queue worker dropped the slot-restore failures that
hal0 bench runalready reported (#2966), so the item left the queue as if it had passed while a slot such asbrainstayed failed. The item now moves to the failed list with a note naming the slot that did not restart, which the Benchmarks queue view shows. (#2979) -
The dashboard health strip and Utilization card no longer show a flat 100% GPU on hosts whose GPU performance level is forced high. #2967 captioned the Settings → Overview row as pinned, but the dashboard iGPU cell and the Utilization card still presented the pinned counter as real load. All three surfaces now format the reading through one shared helper and carry the same "pinned" caption as the telemetry header. (#2974)
-
FLM (NPU) models no longer show up in the model dropdown of GPU and CPU slots, and the API refuses the pairing. The inference card picker, the slot editor and the swap popover filtered only by slot type, so a llama.cpp slot offered FLM models it cannot load, and the Stacks editor listed every model whatever device a stack slot targeted. One shared rule now decides what a slot can bind: FLM models only on NPU slots, GGUF models only elsewhere. The server enforces it too: creating a slot,
PUT /api/slots/{name}/config,loadandswapreturn 400slot.model_engine_mismatchfor an FLM model on a non-NPU slot, or a GGUF model on an NPU slot, instead of accepting it and crash-looping the slot at load. -
Streamed FastFlowLM chat keeps exact token and speed metrics on FastFlowLM 1.0.10. From 1.0.10 a streamed
/v1/chat/completionsresponse carriesusageonly when the request setsstream_options.include_usage, and hal0 never set it, so every NPU stream fell back to estimated token counts and speed. hal0 now sets it on streamed chat requests to NPU slots, merging into anystream_optionsthe client sent. A client that did not ask for usage never sees the extra usage-only chunk, but the metrics row still records it, and the request's stop reason survives it. A client that setinclude_usageitself is passed through unchanged, and other upstreams are untouched. (#2994) -
FLM model pulls and the NPU model catalogue now use the
flmrunner image's FastFlowLM, not the host.deb.flm pullandflm listran the host/usr/bin/flm, and only the installer installs that.deb, so a box that only ranhal0 updatekept an old FastFlowLM that pulled old weights, missed newer tags and disagreed with the container on renamed tags, all into the store the newer runner serves. A pull now runs in a one-shot container of the slot's runner image with the model store mounted where the slot mounts it, and the catalogue asks the running slot first, then a one-shot container, and only then the host binary. A new privileged seam,hal0-flmctl, runs these containers with a pinned entrypoint, as the hal0 account's own uid and groups, with no capabilities and no devices;hal0 updateinstalls it. Cancelling a pull stops and removes its container. An FLM pull now needs theflmrunner image downloaded and says so if it is missing, and creates a store that does not exist yet. The host.debis still used forflm validate. See ADR-0036. (#2993) -
The installer no longer reports "Verify FAILED" when the structured-output probe merely times out. On a CPU-only fresh install the model can take longer than the probe's 120 s to answer, which printed a false failure in the summary box. A curl timeout now prints a distinct warning (model slow or busy) and does not count as a failed probe; an HTTP error or a reply that is not JSON still fails. (#2952)
-
A second text-to-speech slot is now reachable over
/v1/audio/speech. The route only ever resolved the slot namedtts, so a slot created under another name (hal0 slot create voice2 --type tts -m kokoro-v1) loaded and advertised its model but answered every speech request with 404dispatch.no_route. Speech is now routed by slot type: a servingttsslot bound to the requested model serves it, matching the registry id you bound (kokoro-v1) as well as the id the container advertises (kokoro). The slot namedttswins when several match, and a request with no match behaves as before. (#2784) -
A persona edit or switch now reaches the running Hermes. Saving the active persona's name or prompt, and
hal0 agent personas activateor its API route, now render the prompt into Hermes'sconfig.yamlasagent.system_prompt, through the same config merge the provisioner uses. Hermes re-reads that file without a restart, so new sessions and the next gateway turn use the prompt; an open dashboard chat keeps the old one. Before, the change sat in the persona file until a reprovision. The retired reload nudge to127.0.0.1:8765(which nothing listens on) and the "next Hermes restart will pick up the new persona" message are gone: the API returns arefreshobject and the CLI prints what was applied, or that the prompt is not live and the repair command to run.tools_allowedandpreferred_modelare documented as steward-only, and the dashboard persona editor andpersonas showhide them for personas other thanhal0-brain. (#2921, #2850) -
A Hermes gateway migration that fails partway can no longer leave two gateways running or lose track of one. Three gaps remained in the move off a 1.6.0-nightly suffixed
hermes-gateway-<hash>.service(#2946). If an update was killed after taking over the gateway, its crash-recovery journal did not list the gateway, so recovery restarted every unit except it. The restart that loads a rewritten, stale canonical unit ran after the rollback window, so a failed restart left the operator's gateway stopped. And after a failed move,hal0 agent install hermeswent on to enable the canonical unit even when the rollback had restored the suffixed gateway, starting a second poller on the same Telegram/Discord tokens. The journal is now rewritten as soon as the gateway is taken over. The restart runs inside the rollback, which brings the original gateway back if it fails. The install command stops after a failed move. (#2950) -
The omni loop now runs for FLM slots. A chat request for a model served from an FLM slot never matched its slot, because the request carries the native colon tag while the slot config holds the
-FLMcatalog id, so the request silently ran as plain chat. The slot lookup now reads the model id from every model-config shape and matches a-FLMid against its native tag. (#2779) -
Settings → Overview no longer shows a misleading flat 100% GPU utilization on a host whose GPU performance level is forced high. On those hosts the AMD busy counter always reads 100. The Live hardware row now adds "pinned (forced performance level)", as the telemetry header already did. (#2933)
-
A slot whose runner image cannot be resolved now shows "unknown" with a logged reason, not "not configured". When hal0 failed to work out a container slot's runner image (an unreadable
profiles.toml, for example), the slot reportednot-configured, which the docs describe as "not a fault", and nothing was logged. The slot now reportsunknownand the API log carriesslot_view.image_probe_failed ... reason=resolve-error, so a broken config no longer looks healthy. (#2800) -
A bench session no longer leaves pinned slots stopped, and says so if it does. Every Tier-A sweep stopped and restarted each active slot, so a roster session of more than ten sweeps tripped the slot units' start limit and left slots such as
brainandflmfailed, with the bench reporting success. A run of sweeps now shares one exclusive window, slots are restored with a start-limit reset first, and a slot that still cannot be restarted is listed in the session result, logged as an error and makeshal0 bench runexit 1. (#2770) -
The Hermes agent card keeps memory available on boxes with a large memory bank. The overview counted recent writes under a 2-second budget, and on a bank of several thousand facts the count timed out, so the card said "memory is not available" while memory worked. Availability now comes from a quick check; if the count is too slow the card omits the writes figure and memory stays available. (#2957)
-
The
hermes_md_contains_primarycheck now verifies the primary model, and is skipped when none is set.HERMES.mdnever named the primary model, and the check passed on any file that mentionedhal0/agent, so aHERMES.mdwith the wrong model, or none, still read green. The file now carries aPrimary modelline that the check compares with the configured model, and no configured model reports skipped rather than passed. A box shows the new line after its nextHERMES.mdre-render; until then the check warns. (#2947) -
hal0 model update --checknow notices a replaced part of a split GGUF. The check compared only the first part, so a model whose later part was re-uploaded upstream was reported as up to date. It now compares every part, and also reports an update when the number of parts changed. (#2761) -
The Pi agent card shows "unverified" for checks it cannot read, not "missing". When Pi is installed for a home the daemon cannot read (
/root, for example), the provider extension, memory wiring and theme checks have no answer, yet Slot Sync and Dual Memory showed as locked with "the hal0 provider extension is not present" and the theme as "not deployed". They now read "unverified" with the reason, and "not present" is kept for checks that really failed. (#2958) -
hal0 updatenow clears a stale failed Hermes install record. Only the provisioning commands wrote the Hermes provision report, so a box whose last bootstrap failed kept showinginstall: failinhal0 agent statusand on the agent card after an update had rebuilt a healthy release. A converge that leaves a release live on the pin now marks the install phaseokfor that release. Other phases are left alone, and phases older than the live release no longer raise a provisioning warning on the card. (#2914) -
hal0 benchcells no longer all fail on current runner images. The bench passed-mmp 0tollama-bench, which the llama.cpp build in the standard image (b11716) dropped, so every Tier-A cell failed in under a second and the weekly session recorded 24 failed cells and no rows. It now passes-lm none, which also disables mmap and mlock, and the bench seam accepts-lmin place of-mmp. The agent-skill docs now use-lm. (#2769)
Security
-
hal0-agentenvandhal0-benchctlno longer import Python modules from the caller's working directory. Both run as root through thehal0account's sudo grant and started Python with-cbut without-I. sudo keeps the caller's working directory, andpython -cputs that directory first onsys.path, so atempfile.pyorhal0/package planted there ran as root: a local privilege escalation fromhal0to root. Both wrappers nowcd /and start every Python with-I, ashal0-updatealready did. A test now fails if any wrapper underinstaller/wrappers/starts Python without-I. (#3007) -
A dashboard Repair no longer hands the Hermes provisioner every secret hal0-api holds. The repair child inherited hal0-api's whole environment minus
HERMES_HOME, including the box keys, upstream API keys and tokens. Its environment is now built from an allow-list:PATH, locale (LANG,LC_*,TZ), the proxy variables,HOME, and the non-secretHAL0_*settings (includingHAL0_HERMES_LIVE_RESOLVEandHAL0_INFERENCE_BASE) the provisioner needs to reach hal0-api. (#2938) -
A root Hermes repair can no longer be tricked into overwriting a root-owned file. The Hermes provisioner wrote its files through a predictable
<target>.tmptemp file, some of them in directories thehal0service account can write, includingprovision.jsonin the Hermes state directory. That account could plant a symlink at that name, and the nextsudo hal0 agent reprovision hermes --repair(or the installer's post-activation pass) would truncate, rewrite and chown whatever file it pointed to: a local escalation fromhal0to root. Temp files now get a random name opened exclusively without following symlinks, their mode and owner are set on the open file, and a failed write removes them. (#2903) -
Root writes under
/var/lib/hal0and/etc/hal0no longer follow a directory thehal0account swapped for a symlink. Both trees are owned by thehal0service account and are not sticky, so that account could rename.hermes,secrets/,agents/orslots/and put a symlink, or a directory it owns, in its place. A root--repair, the installer,hal0 update,hal0 doctor perms --fixor thehal0-agentenvsudo seam then created, rewrote, chowned or chmodded files inside whatever the link named: a local escalation fromhal0to root. Those writers now walk each directory from/by descriptor, refuse a symlink below any directory someone other than root can write, and require the secrets vault and/etc/hal0/agentsto really be root's before they write into them. The Hermes venv repair and the update cache chown no longer follow the links inside the tree either. An operator-made symlink above the state root, such as/var/lib/hal0pointing at another disk, still works; a symlinked.hermesorsecrets/is now refused. (#2975) -
POST /api/models/{id}/pullrejects a malformed or path-traversing Hugging Face repo or filename before it records anything. The repo and filename fields in the request body (hf_repo,hf_filename,mmproj_filename,draft_repo,draft_filename) were passed on after only trimming whitespace, so a value such as../../etc/x.ggufproduced a registry row whose path pointed outside the models directory — a path that model scans,hal0 model rm --keep-filesand the slot view then trust. Repos must now beorg/nameand filenames must be relative paths inside the repo (subfolders still work); anything else gets a 422model.invalid_sourcenaming the field, and no row is seeded and no download starts. Rows already holding a bad path are not rewritten. (#2775) -
Settings → Secrets and provider credentials accept a TLS trust variable only as a path
hal0cannot write.SSL_CERT_FILE,SSL_CERT_DIR,REQUESTS_CA_BUNDLE,CURL_CA_BUNDLEandNODE_EXTRA_CA_CERTSchoose the CA bundle every outbound TLS client under hal0-api trusts, so pointing one at a bundle thehal0user can edit would let a man-in-the-middle read every token hal0 sends upstream (Hugging Face, provider keys). They are now stored (secrets routes,POST /api/providers/{name}/credentials, and the sharedapi.envwriter) only when the value is an absolute path to an existing root-owned file (a directory forSSL_CERT_DIR) that is not group- or world-writable, under parents that are root-owned and not group- or world-writable, every symlink hop checked (the link, each intermediate hop and the final target), and every entry of anSSL_CERT_DIRdirectory too; anything else is refused withsecret.protected(403,reason: "tls_trust") or a 400 on the credentials route. A custom corporate CA still works once an admin installs it as root. Delete still works on an entry already there, so check Settings → Secrets after updating (#2941). -
Settings → Secrets and provider credentials refuse
SSLKEYLOGFILE. Python'sssl, httpx, curl and Node write every TLS session key to the file it names, so an entry pointing at a filehal0can read let a passive capture decrypt the Hugging Face and provider tokens hal0 sends upstream. It is now refused like the loader variables (secret.protected,reason: "process_control"); delete still works on an entry already there (#2997). -
Settings → Secrets and provider credentials refuse
OPENSSL_CONF,OPENSSL_MODULES,OPENSSL_ENGINESand every otherOPENSSL_*name. An OpenSSL config can load an engine or provider shared object, so an entry ran code inside every OpenSSL user under hal0-api, the same class asLD_PRELOAD. Refused withsecret.protected(reason: "process_control"); delete still works on an entry already there (#2998). -
Settings → Secrets and provider credentials refuse
NODE_TLS_REJECT_UNAUTHORIZED. Set to0, it turned off certificate verification for every Node child of hal0-api (the Hermes web build, npm), a simpler route around the CA-bundle guard than #2941 closed. Refused withsecret.protected(reason: "process_control"); delete still works on an entry already there (#2999). -
Settings → Secrets and provider credentials refuse
XDG_CONFIG_HOMEand every otherXDG_*name. git, pip, uv and npm under hal0-api read their configuration, data and caches from the XDG base directories, so an entry sidestepped the existingHOME,GIT_*,PIP_*andUV_*refusals: git config (core.sshCommand,core.fsmonitor,http.sslVerify=false) runs code or turns off TLS checks,XDG_DATA_HOMEpicks the uv-managed Python the Hermes build runs, andXDG_CACHE_HOMEthe wheels uv and pip reuse. Refused withsecret.protected(reason: "process_control"); delete still works on an entry already there (#3000). -
Settings → Secrets and provider credentials refuse
HTTP_PROXY,HTTPS_PROXY,ALL_PROXY,NO_PROXYand any other name ending in_PROXY, in either letter case. httpx, requests, git, curl, uv and pip honour them, so an entry sent hal0-api's outbound traffic through a proxy chosen by whoever wrote it, and on a plain-http://remote upstream the bearer token went with it. Refused withsecret.protected(reason: "process_control"); delete still works. A box behind a corporate proxy keeps setting these the documented way, by editing/etc/hal0/api.envas root; an entry already in the file keeps working (#3006). -
Settings → Secrets and provider credentials refuse
HF_ENDPOINTand the other Hugging Face endpoint and cache overrides. The ComfyUI model fetch runshf downloadwith hal0-api's environment plusHF_TOKEN, so anHF_ENDPOINTentry sent the operator's Hugging Face token to another host and let that host choose the weights hal0 later loads; a cache directory override served planted files the same way.HF_ENDPOINT,HF_INFERENCE_ENDPOINT,HUGGINGFACE_CO_STAGING,HF_TOKEN_PATH,HF_ASSETS_CACHE,HUGGINGFACE_ASSETS_CACHEand everyHF_HUB_*,HUGGINGFACE_HUB_*andHF_XET_*name are now refused withsecret.protected(reason: "process_control"), and the ComfyUI fetch drops them from its subprocess environment, so an entry written before this update no longer reacheshf downloadeither.HF_TOKENandHF_HOMEare unchanged; delete still works (#3017). -
hal0 doctor allflagsapi.enventries written before these guards. A newapi-env-entriesrow warns on any loader, interpreter, TLS, configuration or proxy variable already in/etc/hal0/api.env(the #2936 names plus #2997–#3000 and #3006), and on a TLS trust variable whose value is not a root-owned pathhal0cannot write (#2941). Updating does not remove an entry, and hal0-api and every child keep inheriting it, so the row is how a box learns that one is there. It names the keys, never the values, and words proxy variables apart, since a box behind a corporate proxy sets them as root on purpose (#2942).