STAGE 03 / ONLINE
Homelab P3
Runner P2–3
~64 credits
Services.
Things start running. Plex, Gitea, Langfuse, SearXNG come up on the homelab. The runner's worker drains the queue end-to-end. Streaming chat appears in the browser at control.davidcockson.com.
Goal
Two services live: homelab stack & runner web/worker.
SearXNG on Tailscale must be reachable from Contabo by the end of this stage — that is the gate into Stage 04.
Exit criteria
- All Prod-Services containers healthy (Plex, Gitea, Syncthing, Langfuse stack, nginx, SearXNG)
- Grafana datasource on Dev-Learning VM
- SearXNG reachable from Contabo via Tailscale IP
- Job submitted to
_queue/appears inrunner-outputs/ - Discord pings on success/failure; trace visible in Langfuse
- Streaming chat works in browser; SQLite history persists
Job lifecycle, end-to-end
Files & commands
Prod-Services compose
Dev-Learning + Ansible roles
## Homelab — Phase 3 Compose Prod-Services and Dev-Learning stacks per spec Phase 3. SearXNG must publish on its Tailscale IP. Add an Ansible task that curls SearXNG from Contabo to verify reachability. ## Runner — Phase 2 (Worker) Build worker per spec. Poller uses shutil.move only. Add the 2-second modification guard for Syncthing. On startup, scan _active/ and re-queue. Each job_type has its own pytest file. ## Runner — Phase 3 (Web + Chat) Build the FastAPI factory + 3 routes per spec. SSE streaming for chat — research the asyncio + httpx chunk pattern before writing. HTMX UI in services/web/static — keep CSS minimal, design later.
What to watch
CRITICAL
Tailscale IP gate
SearXNG must answer on Tailscale before Stage 04 starts. Test with curl from Contabo.
QUIRK
Syncthing partials
Files mid-sync can read truncated. The 2s modification guard exists for this — do not skip.
WATCH
SSE in async
No sync calls inside the streaming generator. httpx.AsyncClient + aiter_lines, not requests.