# Central AI Hub — Configuration & Environment

> **Writing Style**: ASD-STE100 Simplified Technical English (`ste-writing`).

## Secrets

All secrets come from `~/.config/environment.d/10-secrets.conf` on each machine.
Never hard-code a secret. Never commit a secret.

Relevant keys:

| Key | Use |
|---|---|
| `OMNIROUTE_API_KEY` | Model calls through OmniRoute. |
| `PAPERCLIP_API_KEY` | File jobs to Paperclip. The `Hub` agent holds this key (id `108c6220-c9e1-4b62-9e0a-6a763e0d28fd`). Key name: `hub-service`. |
| `TAVILY_API_KEY` | Web search. |
| `OL_API_KEY` | Outline docs. |
| `VIKUNJA_TOKEN` | Vikunja tasks. |
| `GITEA_TOKEN` | Gitea repository. |
| `HUB_TOKEN_PHONE` | Hub token for the phone app. |
| `HUB_TOKEN_ESP32` | Hub token for the house voice. |
| `HUB_TOKEN_DSH` | Hub token for dsh chat. |
| `HUB_TOKEN_CONSOLE` | Hub token for the console portal. |
| `HUB_TOKEN_ADMIN` | Hub token for the admin app. The only trusted door. |

Add the five Hub tokens with `scripts/provision-hub-tokens.sh`. It is
idempotent, keeps mode 600, and never prints a value. A new token needs Sam's OK
(`AGENTS.md` "ask first: credentials").

The container runs as the host user (`HUB_UID`/`HUB_GID`, default `1000:100`),
not the image's `nonroot` user, because the secrets file has mode 0600 and the
`nonroot` user cannot read it. See `docs/outline/04-agent-runbook.md`.

## Hub Environment Keys

The Hub reads these keys from the environment, then from the secrets file. The
secrets file path is `HUB_SECRETS_FILE`, or `~/.config/environment.d/10-secrets.conf`.

| Key | Default | Use |
|---|---|---|
| `HUB_PORT` | `8788` | Listen port. |
| `HUB_ROUTING_FILE` | `routing.md` | Human-edited routing rules. |
| `HUB_SECRETS_FILE` | `~/.config/environment.d/10-secrets.conf` | Read secrets when the environment is empty. |
| `HUB_PROJECTS` | empty | Data projects: `name=url,name2=url2`. |
| `HUB_PROJECT_<NAME>_TOKEN` | empty | Per-project bearer token. |
| `OMNIROUTE_URL` | `http://192.168.20.13:20129` | Model gateway base. |
| `OMNIROUTE_MODEL` | `gpt-4o-mini` | Routing model. The gateway is OpenAI-compatible. |
| `JERVIS_URL` | `http://192.168.20.13:8501` | House TTS. |
| `PAPERCLIP_BASE_URL` | `http://192.168.20.13:3100` | Paperclip board. |
| `PAPERCLIP_COMPANY_ID` | from the repo convention | Company for filed jobs. |
| `HUB_LOG_PATH` | `hub.log` | Append-only request log. |
| `HUB_LOG_MAX_BYTES` | `1048576` | Rotate size. |
| `HUB_LOG_KEEP_DAYS` | `7` | Rotated files kept. |
| `HUB_LLM_TIMEOUT` | `3s` | Routing LLM cap (`docs/hub-api.md` section 8). |
| `HUB_PROJECT_TIMEOUT` | `3s` | One project `/ask` cap. |
| `HUB_DEST_TIMEOUT` | `2s` | One destination command cap. |
| `HUB_TTS_TIMEOUT` | `15s` | House TTS completes out of band. |
| `HUB_ASK_TIMEOUT` | `8s` | `POST /ask` total. |
| `HUB_APPROVE_TIMEOUT` | `5s` | `POST /approve` total. |
| `HUB_RATE_PER_MIN` | `30` | Requests per client token per minute. |

> Observed on 2026-10-10: the routing LLM answer through OmniRoute took 0.8 s to
> 1.9 s with `gpt-4o-mini`, inside the 3 s cap. The `auto/*` combos were slower
> than 3 s and are not the default.

## Services and Ports

| Service | Host | Port | Use |
|---|---|---|---|
| OmniRoute | 192.168.20.13 | 20129 | Model gateway. |
| Paperclip | 192.168.20.13 | 3100 | Job orchestration. |
| Jervis (voice) | 192.168.20.13 | 8501 | Speak and announce. |
| Home Assistant | 192.168.20.30 | 8123 | Devices and timers. |
| MQTT | 192.168.20.13 | 1883 | Voice topics. |
| Apprise | 192.168.20.35 | 8210 | Notifications. |
| Hub | 192.168.20.13 | 8788 | This service. Allocated 2026-10-10. `HUB_PORT`, default 8788. |

> Port 8787 was only a proposal. It is used by the `headroom` container. Port 8788 was free on 2026-10-10 and is the allocated Hub port.

## Config Paths

- Brief: `hub_brief.md`
- Rules: `AGENTS.md`
- Docs: `docs/`
- Outline context: `docs/outline/`

## Rule

A new project gets its own service, its own port, and its own folder. Do not
merge a destination into the Hub.
