Rebuild · Build Atlas DRAWING 03 / 06
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 in runner-outputs/
  • Discord pings on success/failure; trace visible in Langfuse
  • Streaming chat works in browser; SQLite history persists
Drawing 03.A — Queue Flow

Job lifecycle, end-to-end

OBSIDIAN QUEUE · filesystem-as-message-bus _queue/ .md + frontmatter _active/ shutil.move _completed/ success _failed/ explicit error worker · poller → executor → job_types/{text|chain|research} 2s mod-guard · re-queue stuck _active/ on startup · catches all exceptions Ollama Contabo Davas Gemma4 Groq · Gemini runner-outputs/ timestamped .md Discord webhook Langfuse @observe runner-web · FastAPI + HTMX /chat (SSE stream) · /queue (submit, list) · /health (deps) · SQLite history · model picker · Davas indicator cloudflared control.davidcockson.com Atomic moves · explicit failures · no silent fallback. Every transition logged.
Drawing 03.B — Build Detail

Files & commands

Prod-Services compose
  • Plex
  • Gitea + PostgreSQL
  • Syncthing (LAN only)
  • Portainer · Uptime Kuma
  • nginx reverse proxy
  • SearXNG · Tailscale-reachable ★
  • Langfuse · web + worker + ClickHouse + MinIO + PG
Dev-Learning + Ansible roles
  • Prometheus
  • Grafana with Prometheus datasource
  • roles/prod-services deploy
  • roles/dev-learning deploy
  • Uptime Kuma monitors all services
  • Basic Grafana homelab dashboard
  • ★ Verify SearXNG from Contabo
  • worker/poller.py · atomic ops · 2s mod-guard · startup re-queue
  • worker/executor.py · frontmatter dispatch · catches all exceptions
  • job_types/text.py · single provider call → output file
  • job_types/chain.py · sequential, context accumulates
  • job_types/research.py · stub for Stage 04 MCP
  • Discord notifications · success + failure (with reason)
  • @observe on every provider call
  • tests/test_worker/* · atomic ops · routing · happy + failure
  • web/app.py · FastAPI factory + lifespan
  • routes/health.py · Ollama, Davas, Qdrant, Langfuse
  • routes/chat.py · SSE stream · model picker · history
  • routes/queue.py · submit · list active · list recent
  • SQLite chat history · single table
  • HTMX UI · chat panel + queue panel · Davas indicator
  • tests/test_web/*
## 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.
Risks

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.

← Stage 02 · Foundations NEXT · Stage 04
Intelligence →