Hal0 CLI reference

The hal0 CLI is built with Typer. The entry point is
hal0 = "hal0.cli.main:app". Most subcommands are thin HTTP clients over the hal0-api
daemon; a few (config show/edit/migrate/validate, agent install,
memory migrate, capabilities migrate, slot migrate-id-keying) read or write
on-disk state directly and work without a running daemon.

hal0 --help          # top-level help (no args also prints help)
hal0 <group> --help  # per-group help, e.g. hal0 slot --help

Global

Flag Behavior
--version / -V Print hal0 {version} and exit (eager callback).
--help Print help. No flag = help (root app has no_args_is_help=True).
--install-completion / --show-completion Typer’s built-in shell-completion helpers (add_completion=True).

There is no global --verbose or --config flag. Behavior that would normally be a
global flag is controlled per-command via environment variables instead — see
Environment variables (HAL0_API_URL, HAL0_HOME,
HAL0_BIND_HOST, HAL0_PORT, HAL0_FORCE, HAL0_PLAIN, etc).

Top-level commands

Command Purpose Flags
hal0 status System + slot summary. —
hal0 serve Start the hal0 API server (used by hal0-api.service). --host (default 127.0.0.1, env HAL0_BIND_HOST), --port (default 8080, env HAL0_PORT), --reload
hal0 uninstall Uninstall hal0. Conservative by default. --purge, --force/-f (honors HAL0_FORCE=1), --dev (tears down a dev install at $PWD/.hal0ai)
hal0 bench ... Benchmark pipeline. Raw argparse passthrough (allow_extra_args), see Model roster & benchmarks. verbs: plan, run, worker, status, results, history, reindex, devices, publish, eval, bundle, upload
hal0 chat Interactive chat REPL. --model (default "agent"), --base-url (default http://127.0.0.1:8080/v1, env HAL0_CHAT_BASE_URL), --no-stream, --brain (talk to the hal0-brain steward instead)
hal0 system-info Host system info. --json
hal0 ports Port allocation view. --json
hal0 setup First-run provisioning (internal fallback in v1.0 — see caution above). see below
hal0 update ... Check/apply/roll back an update. see below

hal0 chat in-REPL slash commands: /think on|off|default, /clear, /quit / /exit
(Ctrl-D also exits).

hal0 setup

hal0 setup [--auto] [--storage-dir DIR] [--no-pull] [--no-extensions] [--no-slots]
           [--answers FILE] [--emit-answers FILE] [--plan]
Flag Default Meaning
--auto False Non-interactive, applies recommended defaults.
--storage-dir /var/lib/hal0/models Model storage root.
--no-pull False Skip pulling a starter model.
--no-extensions False Skip optional-extension prompts.
--no-slots False Skip seeding starter slots.
--answers unset Path to a hal0-setup.yaml answers file.
--emit-answers unset Write resolved answers to a file and exit, without applying.
--plan (alias --dry-run) False Resolve and print the plan; write nothing.

hal0 update

hal0 update [--channel stable|preview|nightly] [--check] [--rollback]
            [--target VERSION] [--yes|-y] [--restart-slots|--no-restart-slots]
hal0 update status
hal0 update component <id> [--check] [--target REF] [--clear-override] [--yes|-y]
Flag Meaning
--channel Persist the update channel, then check it.
--check Check only; don’t apply.
--rollback Roll back to the previous version. When it predates per-slot tune overrides (ADR-0045), the keys it refuses are removed first: [tune] from slot files, reasoning_effort and reasoning_budget from profiles.toml, and stack entry tune plus embedded profile reasoning keys from stacks.toml. Each changed file is copied to <file>.bak-<stamp> first and listed under tune_stripped in the response. Updating forward does not put the keys back.
--target Pin an exact version. A box below the release’s upgrade_from floor is refused with system.update_upgrade_path_unsupported, which names the floor. From 1.6.0 through 1.7.0 the floor is >=1.4.0; after 1.7.0 it is >=1.5.0. --target pins only the version the channel’s manifest serves now, so it cannot step a box through an older release: a box below the floor runs that release’s installer in place (see Upgrading from an older release).
--yes / -y Skip the confirmation prompt.
--restart-slots Bounce every drifted slot (image and argv drift) and stop.
--no-restart-slots Skip the automatic restart of slots whose runner image changed; the CLI leaves them on the old image (the hal0-api start-up pass is controlled by [updates].auto_restart_image_drift). Cannot be combined with --restart-slots.

An apply that leaves any component below still pending
exits 2 rather than reporting a clean success — distinct from a failed
update (die() → exit 1).

hal0 update status prints the same hal0 version/channel check plus a
per-component table (installed / pinned / status) for the five
release-pinned components — OpenWebUI, runner images, Hermes, Pi, and
Hindsight; their pins ride hal0 releases rather than updating
independently. Exits 2 if any component isn’t converged yet
(pending, stale, or a failure status), so it doubles as a scriptable
convergence gate.

hal0 update component <id> re-runs one component’s converge arm — a
per-component alternative to a full hal0 update for retrying just the
piece that’s stuck. --check prints that component’s status row without
converging it. <id> is one of openwebui, runner-images, hermes,
pi, hindsight (see hal0 update status for the live list).

sudo hal0 update component pi re-pins an installed pi and redeploys hal0’s extensions into ~/.pi without touching the extensions you added or your default provider, model and theme. Unlike the other components it runs in the CLI, not the daemon, so it needs root and not a running hal0-api; run it the same way you installed pi. It refuses when pi’s profile is in a different home or pi is not installed, and takes no --target or --clear-override. It also adds a name-keyed Hindsight bank alias (coding-agent::hal0 to coding-agent::hal0-mono) to an existing ~/.hindsight/coding-agent.json that lacks it, and prints a line when it does. That file is shared: other Hindsight-backed tools on the box read it too. A fresh agent install pi from a git checkout seeds the same alias instead of a path mapping.

A plain hal0 update only reports pi, and the row never affects the exit code: update-available when pi or its adapter is behind the pin (with the command above as a note), newer-than-pin when it is ahead, which the command would downgrade, and unknown when the pin cannot be read. --check needs hal0-api, as for every component, and POST /api/updates/components/pi/converge answers 400.

For hermes, --target SHA builds a vetted commit other than the pin (a full 40-hex commit in VETTED_HERMES_REFS; nothing older than the live release). It persists across hal0 update until --clear-override, and hal0 update status shows the row as override. The pin’s own commit counts as --clear-override. Clearing is refused, keeping the override, when the pin is older than the live release; it cannot check an override this release no longer vets, so that case needs a newer pin. Repair rebuilds the override, not the pin; an unvetted override is never built.

For openwebui, --target DIGEST (sha256:… or bare 64-hex) pins an explicit
digest instead of the release-carried one; it persists across future converges
until --clear-override drops it and converges back to the release pin. There is
no --tag float-to-upstream flag: the pin rides hal0 releases like every other
component. --yes/-y skips the confirmation prompt that --target and
--clear-override show on a terminal. hal0 update owui no longer exists; use
hal0 update component openwebui.

Editing a slot tune

hal0 slot edit NAME accepts --ctx, --thinking/--no-thinking, --reasoning-effort,
--reasoning-budget, --vision, and --speculation. They write sparse
[tune] overrides. Use --tune-reset alone to resume profile inheritance:

hal0 slot edit fast --ctx 8192 --reasoning-effort low
hal0 slot edit fast --tune-reset

hal0 slot — manage inference slots

Enums: --type = llm|embedding|reranking|transcription|tts|image; --provider =
llama-server|flm|kokoro|moonshine; --hardware = vulkan|rocm|cpu.

Command Args Flags
slot list — --json
slot status <name> name --json
slot load <name> name --model/-m, --json
slot unload <name> name --json
slot restart <name> name --json
slot rename <name> <new_name> name, new_name —
slot swap <name> name --model/-m (required), --no-persist, --json
slot logs <name> name --follow/-f (when hal0-api cannot read the journal it prints the reason and returns instead of waiting), --lines/-n (default 200, 1-5000)
slot create <name> name --type/-t (default llm), --provider (default llama-server), --hardware (default: auto-detect), --model/-m (required), --port/-p (default: auto-assign, 1024-65535), --profile (the tune; default: inferred from --type/--hardware, and an llm slot with none runs the chat seed)
slot edit <name> name --model/-m, --port/-p, --provider, --hardware, --profile
slot delete <name> name --force/-f
slot show <name> name --json
slot metrics [name] optional name --json
slot capacity — --json
slot migrate-id-keying — --apply (default: dry-run), --yes/-y, --stop-services

slot create --model and slot edit --model bind only a model hal0 can resolve: a registered
id or a curated catalogue id (the first slot load pulls it). Any other id is refused with
validation.model_not_registered, naming the id, and the slot file is left untouched. NPU,
ComfyUI and the self-managed speech engines (Kokoro, Qwen3-TTS, Moonshine, VibeVoice) are not
checked, since their models are FLM tags, image catalogue ids and baked-in or operator-staged
weights, not registry rows. Register a model file with hal0 model add <path> or hal0 model scan, and list
the registered ids with hal0 model list. A slot file written before this check that
already names an unknown id keeps loading: load serves a registered model of the same capability
in its place and logs a slot.model_default_fallback warning that names both ids.

hal0 model — manage the local model registry

Command Args Flags
model list — --json
model pull <ref> ref --cancel, --draft (a drafter .gguf to download with the model), --draft-repo (the repo holding --draft; default the model’s own)
model default <ref> ref --clear
model update [ref] optional ref --check, --refresh (only meaningful with --check)
model rm <ref> ref --force/-f, --keep-files (remove only the registry entry; files stay on disk, hidden from scans)
model show <ref> ref --json
model scan — —
model add <path> path --id (default ""), --name, --license (default ""), --overwrite, --draft (a drafter GGUF to pair; fit-checked)
model set <ref> ref --draft (a drafter path to pair, or none to unpair and keep the file)
model store [path] optional path --migrate
model run <ref> ref --slot/-s (default ""), --timeout (default: the derived slot-load budget from hal0.slot_lifecycle_budget, 622 at time of writing — see Slot lifecycle timeouts)

hal0 config — inspect and manage configuration

ConfigFile enum: hal0|upstreams|providers.

Command Args Flags
config show [which] optional which (default hal0) —
config edit [which] optional which (default hal0) opens $EDITOR/$VISUAL/vi
config migrate — —
config validate — —
config reload — —
config hardware — --refresh

config validate checks hal0.toml, upstreams.toml, providers.toml and
every slots/*.toml (the typed slot load the daemon runs at boot); it exits
1 if any file fails.

hal0 doctor — diagnostics and repair

Bare hal0 doctor (no subcommand) runs installer/lib/preflight.sh:
--plain (force ASCII, sets
HAL0_PLAIN=1), --ports (space-separated TCP port list override).

Command Flags
doctor verify --json
doctor logs --unit (default hal0-api; hal0 units only: hal0.target, hal0-*, hindsight-api, hermes-gateway, anything else is a 400), --follow/-f, --lines/-n (default 200, 1-5000), --level, --since
doctor toolbox-pull --json, --manifest (probes every runners pin in manifest.json: anonymous ghcr.io pull plus pinned-digest comparison; exit 1 when an image is unreachable or unpublished, 2 when the pin table is missing or malformed)
doctor perms --fix, --force/-f, --table-root (audit old root-era table), --json
doctor models --fix, --force/-f, --json
doctor profiles --json
doctor ports --fix (delete stale netavark DNAT rules found by the audit; privileged)
doctor wrappers --fix (re-install ${LIB_DIR}/bin/hal0-*, the matching /etc/sudoers.d drop-ins, and the /usr/local/bin/hal0(-agent) PATH links from the active tree; privileged)
doctor all --json
doctor bundle --out, --no-rocm-smi, --include-logs (default auto; auto|yes|no)

doctor all rolls up every check into one report. All are read-only except
memory_extraction_throughput, which sends one bounded probe to the extraction slot (at most 512
prompt and 32 decode tokens, capped at 30 s) when the slot’s metrics carry no reading. Each row has a stable id
(Check.key) used in --json output and cross-referenced elsewhere in the docs: api,
dns, runners, capabilities, memory, openwebui, hermes, auth, models,
migrations, ui-dist, api-env-entries, ports, port_dnat, netns, hal0_target, secret-modes,
hardware_freshness, gtt_leak, seed_context_envelope, agent-uid, api-journal, stt-weights, seams,
mcp_mounts, hermes-api, hermes_mcp_auth, hermes_profiles_mcp, hermes_default_memory, hermes_brain_memory, hermes_generation, hermes_command_approvals, hermes_delegation_slot, hermes-gateway-unit, hermes_anchor_window,
hal0_linger, hindsight_llm_auth, memory_engine_version, memory_extraction_throughput, memory_retain_chunk.
hal0_linger reads whether systemd linger is on for the hal0 user (loginctl show-user hal0 -p Linger) and warns when that does not match whether the Hermes gateway is installed.

api-env-entries (“api.env entries”) warns when /etc/hal0/api.env holds a name the Secrets API now refuses to write: a loader, interpreter, TLS, configuration or proxy variable (LD_PRELOAD, PYTHONPATH, SSLKEYLOGFILE, OPENSSL_CONF, NODE_TLS_REJECT_UNAUTHORIZED, XDG_CONFIG_HOME, HTTP_PROXY and the rest of the families listed under Secrets), or a TLS trust variable (SSL_CERT_FILE and the others) whose value is not a root-owned path hal0 cannot write. 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. Remove an entry you did not add in Settings → Secrets, or delete the line as root, then restart hal0-api. Run as a user that cannot read the file, it warns and asks for hal0 or root.

gtt_leak warns of unexplained GPU memory and never refuses a load.

api-journal (“API journal read”) warns until hal0-api.service carries SupplementaryGroups=systemd-journal. hal0 update does not rewrite that unit, so the fix is to re-run the installer once; until then the Logs page and hal0 slot logs --follow report that the journal cannot be read.

The Hermes rows are advisory and never critical. hermes-api (“Hermes API upstreams”) checks, for each served profile, that it has an API key, that its authenticated /v1/models answers 200, and that hal0 has registered the matching upstream. With no key it says to run sudo hal0 agent reprovision hermes --repair. With a missing upstream it says to restart hal0-api or POST /api/agents/hermes/reconcile-upstreams. When the operator turned the gateway off (hermes-gateway.service neither enabled nor running, so the API’s /health is unreachable) the row warns instead of failing once per profile: it says the API path is not checked and names sudo systemctl enable --now hermes-gateway. An enabled gateway that does not answer still fails. hermes_profiles_mcp (“Hermes profile MCP wiring”) warns when a named profile has no hal0-admin and hal0-memory entries, fails when an entry lacks a bearer on a box with auth on, and reports a profile that opted out with hal0: {wire_mcp: false}. hermes_default_memory and hermes_brain_memory check that the X-hal0-Private header on the default and hal0-brain profiles matches hal0: {memory_private: ...} (shared unless it is set; a value that is not a boolean is read as private unless it is false, no, 0 or off, and warned about); the default profile needs sudo hal0 agent reprovision hermes --repair after the opt-in is added by hand. hermes_generation (“Hermes pm generation”) warns when Hermes’s package manager has committed its own environment for the live release without the extras (such as mcp) hal0 built it with. hermes_delegation_slot (“Hermes delegation slot”) passes for the default agent slot and warns when a [hermes] delegation_slot names a slot that is not ready. hermes-gateway-unit (“Hermes gateway unit”) passes when no Hermes agent is installed. With Hermes installed it fails when /etc/systemd/system/hermes-gateway.service is missing, when a gateway run --idle-exit process is running (the row names its pid and cgroup), or when a profile with gateway.standalone: true lacks its own hermes-gateway-<profile>.service. It also fails when the gateway runs from Hermes’s suffixed hermes-gateway-<8 hex>.service instead, which a 1.6.0 nightly fresh install got by running Hermes’s generator as root (#2943), or when both units exist after an interrupted move; sudo hal0 update (or sudo hal0 agent install hermes) moves the gateway onto hermes-gateway.service and keeps its enabled and running state. From 1.6.0 the installer and hal0 agent install hermes write hermes-gateway.service from hal0’s own template and never run Hermes code as root. A unit that is installed but disabled, stopped or masked passes: from Hermes v0.22.3 a profile-scoped dashboard chat and hermes chat are gateway clients, and an installed unit in any state is what stops them from starting a gateway of their own. See Services.

There is no standalone hal0 doctor seams
command — seams is one of these roll-up rows, surfaced by hal0 doctor all.

hal0 upstream — manage upstream LLM providers

Command Args Flags
upstream list — --json
upstream advertise <name> <state> name, state (on|off) —
upstream show <name> name --json
upstream create <name> name --catalog/-c, --url/--base-url, --auth-style (bearer|anthropic|header|none), --auth-header, --auth-env, --timeout, --api-key/-k, --advertise-models/--hide-models (default advertise), --enabled/--disabled (default enabled)
upstream update <name> name partial update — --url/--base-url, --auth-style (bearer|anthropic|header|none), --auth-header, --auth-env, --timeout, --advertise-models/--hide-models, --enabled/--disabled, --model (repeatable), --include (repeatable), --exclude (repeatable), --clear-filters. No --catalog/--api-key — use upstream credentials set to rotate the key.
upstream delete <name> name --force/-f
upstream test <name> name —
upstream credentials set <name> name --key (required, prompts securely if omitted), --env-var

hal0 capabilities — capability-slot configuration

Command Args Flags
capabilities list — —
capabilities set <slot> <child> slot, child --model, --backend, --provider, --enabled/--disabled
capabilities migrate — --apply (default: dry-run)

hal0 comfyui — image models and files

Command Args Flags
comfyui models list — --json
comfyui models install <id>... one or more model ids —
comfyui models remove <id> id --include-shared
comfyui files — —

models install installs the models one at a time, following each download, and exits 1 if any fails. A failure does not stop the models after it. models remove keeps a weight file another installed model loads and lists it; --include-shared deletes it too.

hal0 agent — manage bundled agents

Command Args Flags
agent install <name> name (e.g. hermes) --switch, --gateway/--no-gateway (default gateway), --reset-personas, --terminal-tool/--no-terminal-tool (default: off on a fresh install, preserved on an existing box)
agent uninstall <name> name --keep-memory, --force/-f
agent list — --json
agent peers — —
agent status [name] optional name (default hermes) --json
agent log [name] optional name (default hermes) --phase
agent dashboard-token [name] optional name (default hermes) —
agent reprovision [name] optional name (default hermes) --repair, --offline (never builds; a needed build fails and names the step that needs the network), --verbose/-v
agent command-approvals [mode] optional mode (smart or manual); omit to show the setting --json
agent approvals list — —
agent approvals approve <approval_id> approval_id; needs root, on the box —
agent approvals deny <approval_id> approval_id; needs root, on the box —
agent personas list — —
agent personas show <persona_id> persona_id —
agent personas activate <persona_id> persona_id --no-apply

hal0 update component hermes builds and activates a fresh Hermes release of the pin, or of --target <sha>, through the same release builder as install and hal0 update, and switches back to the previous release by itself if the new one fails its re-check. A --target commit persists across hal0 update until --clear-override.

hal0 pins Hermes v0.22.4 (4d04303818b61bf3aa09278af48356315515965b). --target is refused when the ref is older than the live release: once v0.22.4 is live, --target d923a5eb487a17c0a68801bba9ef0f56c81040a8 (v0.22.2) fails with a downgrade error, because v0.22.3 and later write gateway runtime state into state.db that v0.22.2 cannot run over. The automatic flip-back after a failed activation is not a build and is unaffected.

agent reprovision hermes --repair forces every provisioning phase, builds a fresh Hermes release through the same builder as hal0 update component hermes, rewrites the persona seeds and config.yaml, then re-registers the Hermes profiles as /v1 upstreams. Run sudo hal0 agent reprovision hermes --repair once on a box where Hermes was installed before 1.6.0 so the default profile gets its API key and appears in /v1/models and Open WebUI. The same run wires hal0-admin and hal0-memory into every named profile (phase profile_mcp_wire) and writes any missing profile API key (phase profile_api_keys).

agent dashboard-token hermes prints the token Hermes Desktop needs for a remote pairing in token mode. hal0-agent@hermes mints it once under /var/lib/hal0/state/agents/hermes/ and exports it to the dashboard as HERMES_DASHBOARD_SESSION_TOKEN at every start, so the pairing survives restarts and hal0 update. Run it as root or as the hal0 user. A systemctl edit hal0-agent@hermes drop-in that sets HERMES_DASHBOARD_SESSION_TOKEN wins over the file.

agent uninstall hermes also runs loginctl disable-linger hal0 (as root; otherwise it prints the command), undoing the linger the Hermes gateway turned on.

If the daemon reports a partial uninstall, the command lists the residual paths and why removal failed, gives cleanup guidance, and exits with status 1. Review and remove those files with sufficient permissions, then re-run the uninstall.

agent install hermes respects an operator-disabled gateway. If hermes-gateway.service is disabled, the unit file is refreshed with --no-start-now --no-start-on-login and the unit is not enabled or started (a copy already running is left running). If it is masked-runtime it is treated the same way. If it is persistently masked, the generator does not run at all. Either way the command prints how to turn it back on (systemctl enable --now hermes-gateway.service, preceded by systemctl unmask or unmask --runtime for a mask). A fresh or enabled gateway is installed and enabled as before.

While the terminal tool is off (the default; --no-terminal-tool or HAL0_HERMES_TERMINAL=0), hal0 writes terminal.backend: hal0-terminal-off into the Hermes root config.yaml and into every profile it wires. Hermes rejects that backend in the terminal tool’s own availability check, so terminal and process_manage leave every tool schema, including the one built for the gateway’s preview-restart side agent, which ignores agent.disabled_toolsets from Hermes v0.22.3. Cloned profiles also lose execute_code and delegate_task while the terminal is off. A docker or ssh backend you chose is saved under hal0.terminal_backend_saved and restored when you turn the terminal back on. hal0 update applies the gate to an existing box and restarts Hermes when terminal.backend changed. Hermes logs Unknown TERMINAL_ENV 'hal0-terminal-off' at ERROR about every 30 seconds while tools are listed; on a terminal-off box that line is expected.

hal0 app — manage optional apps

Command Args Flags
app install <name> name (currently only openwebui) —
app list — —
app uninstall <name> name --force/-f

Runner images, profiles, MCP, auth and the rest

The memory, runner-images, profile, mcp, bridge, auth, network, oauth and
board sub-apps, the slot lifecycle timeouts, and the separate hal0-agent binary have
their own page,
hal0 CLI reference: memory, runner images, profiles, MCP, auth & more.