- Three inbound routes from docs/hub-api.md: POST /ask, POST /approve, GET /health. - Five-step pipeline in src/hub, with packages hear, understand, check, route, answer. - Routing through OmniRoute with the capability list and routing.md. The never-route control check runs before any LLM call. - One real destination: Jervis house TTS, started out of band. - Fake /ask project (src/tests/fakeproject) for the round-trip test. - Append-only, size-capped request log. Bearer auth, source match, rate limit. - Tests: all packages pass, including the fake /ask round trip. Race clean. - Dockerfile and compose updated to mount routing.md and secrets, log on tmpfs. Co-Authored-By: Paperclip <noreply@paperclip.ing>
5.9 KiB
Central AI Hub — AI Agent Runbook
Writing Style: ASD-STE100 Simplified Technical English (
ste-writing).
Phase 1 — Prove the Paperclip Round Trip
This phase has no Hub code. It proves the missing link: a job that leaves and a result that returns.
Steps
- Create a Paperclip agent API key for this project. DONE.
- Paperclip board:
http://192.168.20.13:3100. - Agent:
Hub, id108c6220-c9e1-4b62-9e0a-6a763e0d28fd. - Key name:
hub-service. - The key is stored in
/home/sam/.config/environment.d/10-secrets.confasPAPERCLIP_API_KEY. Never commit it. - Verification:
GET /api/agents/mewith the key returns200and the nameHub. - Note:
POST /api/agents/{id}/keysis board-only. A run JWT gets403 Board access required. Mint keys with the local board key.
- Paperclip board:
- File one job with the round-trip script. The script uses the issue-create
endpoint:
POST /api/companies/{companyId}/issues.- The old
POST /api/issuesroute does not exist. It returns404. - Company id:
d4921013-e5da-4d88-8016-57d5746e2d03. - Script:
scripts/paperclip-roundtrip.sh.
- The old
- Confirm the job appears in the Paperclip board. DONE.
- The first job is
AUD-55, idd58891ce-9934-4710-9bba-d37974251ed9.
- The first job is
- Wait for the agent to close the job. DONE.
AUD-55closed asdoneat2026-10-10T06:21:03Z.- The agent comment on
AUD-55: "The Hub round trip works."
- Send the result to Jervis
POST /announceso the house speaks it. DONE.- See the announce call and the response below.
- Record the result in this file. DONE.
Commands
# File one job with the script. It reads PAPERCLIP_API_KEY from the
# environment, then from ~/.config/environment.d/10-secrets.conf.
scripts/paperclip-roundtrip.sh \
--title "Hub round-trip job: confirm the return path" \
--description "Prove the outbound Paperclip path." \
--assignee 403de1f7-645c-4d35-bc7e-19b01d77bacc \
--priority high \
--project 7ae791bb-8d9a-462f-99bf-013421d20623
# Speak the result.
curl -sS -X POST http://192.168.20.13:8501/announce \
-H "Content-Type: application/json" \
-d '{"text":"The Hub round trip works."}'
Recorded Result — 2026-10-10
The round trip works. This table holds the proof.
| Item | Value |
|---|---|
| Key source | PAPERCLIP_API_KEY in /home/sam/.config/environment.d/10-secrets.conf |
| Key name | hub-service |
| Agent | Hub, id 108c6220-c9e1-4b62-9e0a-6a763e0d28fd |
| Job identifier | AUD-55, id d58891ce-9934-4710-9bba-d37974251ed9 |
| Job result | "The Hub round trip works." |
| Job final status | done, at 2026-10-10T06:21:03Z |
| Announce call | POST http://192.168.20.13:8501/announce |
| Announce response | HTTP 200, text "The Hub round trip works." |
The script wrote this line to .state/paperclip-roundtrip-issues.log:
2026-10-10T06:20:48Z d58891ce-9934-4710-9bba-d37974251ed9 AUD-55
Note: Jervis has no playback-telemetry endpoint. Thus the HTTP 200 code and the spoken reply are the strongest available proof of the announce. The announce ran one time.
Definition of Done
- The job appears in Paperclip. DONE.
- The job closes. DONE.
- The house speaks the result. DONE.
- This file records the result. DONE.
Phase 2 — Hub Skeleton (AUD-49)
This phase builds the Hub itself. The shape is frozen in docs/hub-api.md. The
code layout is in docs/outline/03-code-map.md.
What was built
POST /ask,POST /approve, andGET /health.- The five steps:
hear→understand→check→route→answer. - Routing through the LLM at OmniRoute, with the capability topics and
routing.md. - The never-route check runs before the LLM. A control action never reaches a model.
- One real destination: the house speakers (Jervis
/announce). - A fake
/askproject for tests and manual runs.
Test command
docker run --rm -v "$PWD/src":/src -w /src golang:1.25-alpine go test ./...
Result on 2026-10-10: all packages pass, including the fake /ask round trip.
Live round trip
The Hub ran on port 18788 with HUB_PROJECTS="compass_nhs=http://127.0.0.1:9199"
and the fake project on 9199. The real OmniRoute LLM chose the project. Jervis
spoke the reply on the house speakers.
Request:
{
"text": "what does Harry have on today?",
"asker": "sam",
"source": "esp32",
"device": "kitchen-esp32",
"request_id": "01AUD49PROOF"
}
Response:
{
"status": "ok",
"request_id": "01AUD49PROOF",
"answer": "Harry has maths and a music lesson today.",
"shape": "speech",
"speech": {"text": "Harry has maths and a music lesson today.", "target": "house"},
"route": {"kind": "project", "project": "compass_nhs"}
}
Hub stdout (evidence):
router: model=gpt-4o-mini status=200 elapsed=1.554598547s
speakers: announced 41 chars
The routing LLM answered in 1.5 s (inside the 3 s cap). The house announcement ran out of band and Jervis returned HTTP 200. Jervis has no playback telemetry, so the HTTP 200 and the spoken reply are the strongest available proof.
Control action proof
"cut the kids internet" returned status: "refused", route.kind: "refused",
route.reason: "high_consequence_control". No LLM call ran for that request.
The unit test TestControlActionNeverReachesLLM asserts the zero call count.
Deploy note
The docker-compose.yml mounts routing.md and the secrets file read-only, and
uses a /tmp tmpfs for the log. To deploy: build the image, then restart the
container. A HUB_TOKEN_* key per client must exist in the secrets file first.
After Phase 1
Follow the build order in AGENTS.md. Do not start a destination before the
round trip works.
Health Checks
curl -s http://192.168.20.13:3100/health # Paperclip
curl -s http://192.168.20.13:8501/health # Jervis
curl -s -o /dev/null -w '%{http_code}\n' http://192.168.20.30:8123/ # HA