Connect external providers
hal0 can route requests to external upstreams — third-party
OpenAI-compatible APIs like OpenAI, Anthropic, OpenRouter, Google AI
Studio, Ollama, or any custom endpoint. You wire them up by editing
/etc/hal0/upstreams.toml (the source of truth) and then writing the
credential and testing reachability through the API or dashboard.
See what’s available
Section titled “See what’s available”The integration catalog lists the known provider templates the “add upstream” form draws from:
curl http://localhost:8080/api/providers/catalogIt includes OpenAI, Anthropic, OpenRouter, Google AI Studio, Ollama, and a generic custom OpenAI-compatible entry, each with its default base URL and auth style.
Define the upstream
Section titled “Define the upstream”Add a [[upstream]] block to /etc/hal0/upstreams.toml:
[[upstream]]name = "openrouter"kind = "remote"url = "https://openrouter.ai/api/v1"auth_style = "bearer"auth_value_env = "OPENROUTER_API_KEY"timeout_seconds = 300.0advertise_models = trueThe fields:
-
name— a unique id you’ll reference everywhere else. -
kind—remotefor an external provider (slotis reserved for local container slots, which register themselves). -
url— the base URL (required, non-empty). -
auth_style— how the key is sent. Hand-writtenupstreams.tomlentries usebearer,header, ornone. The built-in cloud presets add two more, applied automatically when you pick them:anthropic(x-api-key+anthropic-version, for Anthropic) andgoogle_query(?key=on the URL, for Google AI Studio). -
auth_value_env— the name of the environment variable that holds the secret. The key itself is never written to TOML — only the env-var name lives here. -
timeout_seconds— request timeout (default300). -
advertise_models— whether this upstream’s models appear in the aggregated model list (defaulttrue).
Write the credential
Section titled “Write the credential”The secret lives in /etc/hal0/api.env (a systemd EnvironmentFile),
not in the TOML. Write it through the API, which binds the credential to
the upstream’s declared auth_value_env so you can’t accidentally write a
key the upstream won’t read:
curl -X POST http://localhost:8080/api/providers/openrouter/credentials \ -H 'content-type: application/json' \ -d '{"key":"OPENROUTER_API_KEY","value":"sk-or-..."}'The key must match the upstream’s auth_value_env exactly and be a
valid ALL_CAPS env-var name. The endpoint writes the value atomically
to api.env with 0600 permissions, updates the running process
environment so the change takes effect immediately, and never echoes
the secret back — the response carries "value": "***REDACTED***".
List configured providers
Section titled “List configured providers”# remote providers onlycurl http://localhost:8080/api/providers
# all upstreams (remote + local slots)curl http://localhost:8080/api/upstreams
# one upstreamcurl http://localhost:8080/api/upstreams/openrouterEach entry reports auth_configured (whether the env-var is set) but
never the secret value.
Test the connection
Section titled “Test the connection”curl -X POST http://localhost:8080/api/upstreams/openrouter/testThis probes the upstream’s /models endpoint (appended to the configured base URL) and returns a reachability report:
{ok, status, latency_ms, models_count, error?}. An unhealthy result also
fires a dashboard footer event so the outage is visible. The dashboard’s
Providers tab exposes the same probe behind a “test connection” button.
Picking up changes
Section titled “Picking up changes”Credential writes through the API apply to the running process
immediately. Edits to upstreams.toml (adding, removing, or retiming an
upstream) are read at startup — restart hal0-api or reload upstreams to
pick them up. The credential-write response includes that hint.