Skip to content

Security posture

hal0 makes one security decision explicit and central: it does not ship built-in authentication or TLS. The API binds 0.0.0.0:8080 and every endpoint is open to anything that can reach that port. This is a deliberate design choice (ADR-0012), and the rest of your security posture is built on top of it rather than inside it.

Earlier designs bundled an auth layer and a TLS-terminating proxy. That was removed (ADR-0012) because it duplicated, badly, what every operator already runs better elsewhere: a real reverse proxy. Bundling auth meant maintaining credential storage, token rotation, and TLS certificate plumbing inside the inference server — surface area that is someone else’s core competency.

Instead, hal0 assumes one of two postures:

  • Single trusted LAN — hal0 is reachable only from machines you trust, and the open API is acceptable.
  • Reverse proxy in front — you terminate TLS and enforce authentication at a proxy you control, and only the proxy talks to hal0.

To add TLS and authentication, place a reverse proxy of your choice — Traefik and nginx are common choices — between your clients and hal0-api. The proxy owns:

  1. TLS termination — present a certificate for your chosen hostname and terminate HTTPS at the proxy.

  2. Authentication — enforce whatever you need (basic auth, OAuth/OIDC, mTLS, an allowlist) before a request is forwarded.

  3. Forwarding — proxy the authenticated request to hal0-api on 127.0.0.1:8080 (or its LAN address), and bind hal0 itself so only the proxy can reach it.

This is the standard “auth at the edge” pattern. hal0 stays a pure inference and control plane; your proxy is the single, well-understood place where access control lives.

The one place hal0 does enforce a network gate is the MCP mount. The admin and memory MCP servers are mounted as sub-applications under /mcp/admin and /mcp/memory, and the MCP transport applies DNS-rebinding protection.

MCP tab showing tool and resource inventory The dashboard’s MCP tab displays the live admin and memory server status.

By default that protection is localhost-only: only 127.0.0.1, localhost, and [::1] (any port) are accepted as Host headers and request origins. A client reaching the mount from any other host gets a bare 421 Invalid Host header response. Because hal0 binds 0.0.0.0 on a LAN, you usually need to widen this to reach /mcp/* from another machine or through your proxy:

  • HAL0_MCP_ALLOWED_HOSTS — a comma-separated list of host, host:port, or host:* values added to the localhost allowlist. The single value * disables DNS-rebinding protection entirely (the fully-open posture some LAN-only deployments want).
  • HAL0_MCP_ALLOWED_ORIGINS — a comma-separated list of browser origins. When left unset, http and https origins are derived automatically from each host you add, so the dashboard and a reverse-proxy vhost work without a second knob.

This is a host/origin gate, not authentication — it stops a browser on another site from rebinding DNS to reach your loopback MCP server. Real access control still belongs at your reverse proxy.

hal0’s agent and memory surfaces don’t use bearer tokens for identity. Instead, a caller’s identity rides on the X-hal0-Agent request header (validated to a bounded [a-zA-Z0-9_-] id), and the same identity resolves the same memory namespace whether it arrives over REST or MCP. Privileged or destructive agent actions are gated: rather than executing immediately, they enqueue an approval that a human clears from the dashboard, the CLI, or via the approval API. See Agents for how personas, tool gating, and the approval queue fit together.

Keep it off the internet

Never bind the open API where untrusted clients can reach it. Front it with a proxy or keep it LAN-only.

Terminate TLS at a proxy

Add HTTPS and auth at Traefik / nginx; let only the proxy reach hal0.

Scope the MCP allowlist

Widen HAL0_MCP_ALLOWED_HOSTS only to the hosts that must reach /mcp/*; avoid * unless the whole LAN is trusted.

Treat memory as opt-in

The memory subsystem is disabled unless you enable it; keep it off if you don’t need it. See Memory.