Files
central_ai_llm_master/README.md
Sam Rolfe 4d87124840 AUD-56: deploy prep - token script, container user fix, runbook
- Add scripts/provision-hub-tokens.sh. It adds the five HUB_TOKEN_* keys to
  ~/.config/environment.d/10-secrets.conf. It is idempotent, keeps mode 600,
  leaves a mode-600 backup, and never prints a value. It uses openssl when
  present and /dev/urandom otherwise (this host has no openssl).
- docker-compose.yml: run the container as the host user. The image's
  distroless nonroot user cannot read the 0600 secrets file. With nonroot the
  Hub read no tokens and every /ask returned 401.
- Document the deploy steps, the finding, and the dry-run evidence in
  docs/outline/04-agent-runbook.md, docs/outline/02-config-and-env.md, README.md.

Dry run on port 18788 with a scratch secrets file: /health ok with
omniroute ok, control action refused, bad token 401. The host secrets file and
the running hub container were not touched.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-10 17:48:22 +11:00

119 lines
3.9 KiB
Markdown

# Central AI Hub
One small service that every way in reaches every ability through. Built once,
shared by the phone app, the house voice, chat, and the admin app.
## Demonstrated Skills & Tech Stack
- **Core Technologies**: Go 1.25 service · HTTP JSON API · MQTT · LLM
intent routing · OmniRoute gateway · Docker
- **Entry routes**: `POST /ask` · `POST /approve` · `GET /health`
(frozen in `docs/hub-api.md`)
- **Key Tools**: Jervis (voice) · Home Assistant · Paperclip · Artemis ·
internet use control · Apprise / NTFY · OmniRoute
- **Engineering Capabilities**: service design · intent routing · authorization
and approval flows · event-driven integration · deterministic safety for
high-consequence actions
## Quick Start
### Run the Hub
```bash
# Build and run the container (Docker host 192.168.20.13).
docker compose up -d --build
# Liveness (no token).
curl -s http://192.168.20.13:8788/health
# Ask a question (source esp32 speaks the reply on the house speakers).
curl -s -X POST http://192.168.20.13:8788/ask \
-H "Authorization: Bearer $HUB_TOKEN_ESP32" \
-H "Content-Type: application/json" \
-d '{"text":"what does Harry have on today?","asker":"sam","source":"esp32"}'
```
The Hub reads `HUB_TOKEN_*`, `OMNIROUTE_API_KEY`, and the project tokens from
`~/.config/environment.d/10-secrets.conf` (mounted read-only, never committed).
Add the five Hub client tokens with the idempotent script
`scripts/provision-hub-tokens.sh`; it keeps mode 600 and never prints a value.
A new token needs Sam's OK (`AGENTS.md` "ask first: credentials").
### Test without a network
```bash
scripts/hub-test.sh
```
The fake `/ask` project in `src/tests/fakeproject/` proves the round trip.
### Phase 1 — prove the Paperclip round trip
```bash
# 1. Create an agent API key in Paperclip.
# 2. Export it for the test only.
export PAPERCLIP_API_KEY=<redacted>
# 3. File one job.
scripts/paperclip-roundtrip.sh --title "round-trip test"
```
Full steps: `docs/outline/04-agent-runbook.md`.
## System Architecture
```mermaid
flowchart LR
subgraph IN["Ways in"]
PHONE["Phone app<br/>wake word + voice"]
ADMIN["Admin app<br/>restricted actions"]
ESP["ESP32 voice<br/>Hey Jervis"]
DSH["dsh chat"]
CONSOLE["Console portal"]
end
HUB{{"HUB<br/>hear - understand - check - route - answer"}}
subgraph OUT["Peers and services<br/>(the Hub is not a destination)"]
HA["Home Assistant"]
SPK["Speakers<br/>Jervis TTS"]
NET["Internet control<br/>parental limits"]
ART["Artemis<br/>phone screen"]
PC["Paperclip<br/>work bench"]
WEB["Web search"]
APP["Apprise / NTFY"]
end
PHONE --> HUB
ESP --> HUB
DSH --> HUB
CONSOLE --> HUB
ADMIN --> HUB
HUB --> HA
HUB --> SPK
HUB --> ART
HUB --> WEB
HUB --> APP
HUB -- "ask only (query)" --> NET
HUB -- "slow job (approved)" --> PC
ADMIN -- "control + tap to approve" --> NET
PC -. result .-> HUB
```
*Interactive architecture map:* [maps.lab.audasmedia.com.au/central_ai_llm_master](https://maps.lab.audasmedia.com.au/central_ai_llm_master/docs/)
## Usage & Configuration
- **Model calls** go through OmniRoute at `http://192.168.20.13:20129/v1`.
- **Secrets** come from `~/.config/environment.d/10-secrets.conf`.
- **Paperclip** is at `http://192.168.20.13:3100`. An API key is required.
- **Home Assistant** is at `http://192.168.20.30:8123`.
- **Jervis** (voice) is at `http://192.168.20.13:8501`.
- **Hub** is at `http://192.168.20.13:8788` (local Docker container).
The design brief is `hub_brief.md`. Project instructions are `AGENTS.md`.
The interface between the Hub and every data project is `docs/ask-contract.md`.
The frozen inbound API (ways in to the Hub) is `docs/hub-api.md`.
Every other project is a **peer**, not a sub-project. A high-consequence control
(for example, cutting the kids' internet) does **not** route through the Hub.