documents-ingest-cyclic (jobs/documents-ingest/src/documents_ingest/cyclic_ingest.py): orkiestruje paperless_adapter -> chunk_embed -> summarize(--backend anthropic, claude-haiku-4-5) -> summarize(--embed-summaries) bez zmian w samych jobach. Ollama@SOLARIA (availability_target: medium) jest tolerowana offline: probe GET /api/tags przed obu etapami embed, brak -> pominięcie, nie fail (oba embed passy idempotentne, nadrobią się na kolejnym ticku). Czwarty etap (embed-summaries) dopisany ponad plan §7.1 (który wymieniał tylko 3 kroki) — bez niego nowe streszczenia miałyby embedding=NULL i byłyby niewidoczne dla cascade_query (bramka kroku 4, WHERE embedding IS NOT NULL); potwierdzone z Oskarem. Predykaty pass/fail każdego etapu 1:1 z exit-checkiem danego joba (chunks_errors, llm_errors, stats-balance itd.) — etapy izolowane, nie fail-fast (wcześniejszy fail nie blokuje kolejnych, tak jak joby izolują błędy per wiersz). Metryki .prom (atomowy zapis, last_success_timestamp trzymany z poprzedniego pliku przy failu) do /opt/homelab/state/node-exporter/kb-ingest.prom. 36 nowych testów (202/202 pakietu). systemd (jobs/documents-ingest/systemd/): pierwszy systemd-timer w repo — kb-ingest.timer (OnCalendar=*-*-* 03:30, Persistent=true, plan §7.1) + kb-ingest.service (host-level, User oskar, EnvironmentFile /opt/homelab/kb/.env) + kb-ingest-run.sh (log per-run do /opt/homelab/logs/kb-ingest/, konwencja repo). Instalacja i sekrety udokumentowane w README (Faza 3 krok 5) — instalacja na PIHA dopiero po merge. fleet-prometheus (rules/kb-ingest.yml): KbIngestStale (>172800s od last_success, critical) + KbEmbedBacklogGrowing (backlog>0 przez 72h, warning) — dostawa istniejącym torem brain-watchdog->Telegram, bez Alertmanagera (konwencja liveness.yml). node_exporter: owner_node vps -> per-host (service.yaml) + wpis + override (--collector.textfile.directory, bez nowego mountu — czyta przez istniejący /:/host:ro) + topology.yaml dla PIHA. Domyka pozycję z docs/backlog.md "stability-agent / node_exporter owner_node single, biegaja wielomiejscowo -> per-host" (połowę — node_exporter; stability-agent zostaje osobnym follow-upem) w ramach paczki B inwentaryzacji monitoringu dla PIHA. Test end-to-end na żywo na PIHA (2× --apply, po potwierdzeniu z Oskarem): pierwszy run złapał 5 dokumentów faktycznie nowych w Paperless (nieoczekiwane, niezwiązane z tym taskiem) -> 82 nowe chunki (2 ocr_junk), 5 nowych streszczeń, 5 embeddingów streszczeń, 0 błędów, metryki zapisane. Drugi run: pełna idempotencja, wszystko 0. ANTHROPIC_API_KEY dodany przez Oskara ręcznie do /opt/homelab/kb/.env (nigdy nie logowany/generowany). Co dalej: prawdziwa instalacja systemd (services.yaml już przygotowany, po merge), zdecydowanie czy stability-agent też idzie na per-host przy okazji. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| rules | ||
| docker-compose.yml | ||
| env.example | ||
| healthcheck.sh | ||
| prometheus.yml | ||
| README.md | ||
| service.yaml | ||
fleet-prometheus
Prometheus instance that is the source of truth for fleet liveness. Runs on the VPS (ingress / control-plane host) and is reachable only over Tailscale.
This is a clean scaffold: right now it scrapes only itself and the VPS-local
node_exporter. The fleet targets, liveness rules, and alerting integration are
deliberately separate, later steps (see Next steps below).
Why separate from the home prom
The home prom on PIHA is LAN-only and monitors the home network. This
instance is intentionally a distinct Prometheus dedicated to the distributed
fleet — hence the fleet- naming. The split is made explicit in every series:
external_labels: { fleet: "homelab-codex" }- job names prefixed/scoped for the fleet (
prometheus,fleet-node)
It also avoids two anti-patterns the home prom has:
- No
:latest— the image is pinned toprom/prometheus:v3.5.0(LTS). - No inline secrets —
prometheus.ymlis secret-free by contract (the home prom embeds a plaintext HAOS token; we do not).
Stack
| Container | Image | Purpose |
|---|---|---|
fleet-prometheus |
prom/prometheus:v3.5.0 |
Fleet liveness Prometheus (9090) |
Placement & exposure
- Node: VPS (
ubuntu-4gb-hel1-1, Tailscale100.95.58.48). - Exposure:
tailscale-internal— exposed exactly likecontrol-plane: a plain published9090:9090port with no public npm reverse-proxy entry. Reachability is constrained to the Tailscale mesh at the VPS firewall layer. Do not add this to npm or public DNS. - Memory:
mem_limit: 512m.oom_score_adj: 200(positive) — fleet monitoring is less critical than the control-plane (-900), so it is a sacrificial OOM victim before the control-plane, never after. - Retention:
15d/2GB(whichever hits first) — tight on purpose.
Data
TSDB lives in the named volume fleet-prometheus_tsdb → /prometheus.
Configuration
prometheus.yml— committed, secret-free. Global config + two scrape jobs.env.example→ copy to.env(gitignored). HoldsTAILSCALE_BIND_IP, the VPS Tailscale interface IP the listen socket is bound to.
Deploy — .env is mandatory
The port is published as ${TAILSCALE_BIND_IP}:9090:9090 so the socket is bound
only to the Tailscale interface, never 0.0.0.0. Compose auto-loads a
co-located services/fleet-prometheus/.env, but if that file is missing the
variable resolves to a blank string and Compose silently binds to 0.0.0.0
(publicly reachable on the Hetzner IP) — it only warns, it does not fail.
Therefore, before up -d:
-
Create
services/fleet-prometheus/.envfromenv.examplewith the realTAILSCALE_BIND_IP(verify withtailscale ip -4on the VPS). -
Deploy with the env file explicit, to be safe:
docker compose -f services/fleet-prometheus/docker-compose.yml \ --env-file services/fleet-prometheus/.env up -d -
Confirm the bind is Tailscale-only (must NOT show
0.0.0.0):docker compose -f services/fleet-prometheus/docker-compose.yml \ --env-file services/fleet-prometheus/.env config | grep host_ip
Scrape targets (scaffold)
| Job | Target | Notes |
|---|---|---|
prometheus |
localhost:9090 |
self |
fleet-node |
host.docker.internal:9100 |
VPS node_exporter (runs network_mode: host, listens on host :9100); reached via host-gateway |
Verify
# Config is syntactically valid (uses promtool inside the image):
docker run --rm -v "$PWD/services/fleet-prometheus/prometheus.yml":/etc/prometheus/prometheus.yml:ro \
prom/prometheus:v3.5.0 promtool check config /etc/prometheus/prometheus.yml
# Compose renders:
docker compose -f services/fleet-prometheus/docker-compose.yml config
# After `up -d`:
curl -sf http://localhost:9090/-/healthy
curl -s http://localhost:9090/api/v1/targets | jq '.data.activeTargets[].health'
./services/fleet-prometheus/healthcheck.sh
Next steps (NOT done in this scaffold)
- Fleet targets: add the
100.xTailscalenode_exporters for SATURN, SOLARIA, PIHA, CHELSTY to thefleet-nodejob. - Liveness rules:
rule_fileswithup == 0 for: 5m(placeholder is commented inprometheus.yml). - Alerting: Alertmanager wiring, and/or
brain-watchdogquerying the firing alerts API → Telegram (placeholder commented inprometheus.yml).