homelab-codex-ws/kb/incidents/2026-08-26-mail-sync-20-dni-ciszy.md
oskar 4fa10f0a72 docs(kb): incydent 20 dni ciszy kb-mail-sync + session log
Dwa niezalezne root cause: PermissionError na save_eml (archiwum
root:root po sudo-runach 06.08) + fleet-prometheus nigdy nie dostal
/-/reload po dodaniu regul kb-mail-sync.yml/kb-ingest.yml (zylo tylko
fleet-liveness). Naprawa: chown archiwum + 2 tick recovery (548 kopert,
0 bledow) + reload Prometheusa z weryfikacja lancucha alertowego
end-to-end (brain-watchdog->Telegram wpiety poprawnie).
2026-08-26 20:39:45 +02:00

8.3 KiB
Raw Blame History

okf type visibility status updated links
0.1 incident private active 2026-08-26
../audits/mail-sync-2026-08-06.md
../services/job-mail-imap-sync.md
../runbooks/mail-sync-run.md
../../docs/sessions/2026-08-06-kb-mail-sync-live.md
../../docs/sessions/2026-08-26-kb-incydent-mailsync.md

Incydent: kb-mail-sync bez zapisu przez 20 dni, niewykryty — 2026-08-26

Status: oba root cause ustalone i naprawione. Przyrostówka odzyskana, alerting po raz pierwszy realnie wpięty (wcześniej istniał tylko w repo, nie w runtime). Dotknięty serwis: mail-imap-sync (job) @ PIHA + fleet-prometheus @ VPS. Klasa: dwa niezależne błędy nakładające się w czasie — awaria zapisu (uprawnienia) + martwy monitoring (config nigdy nie przeładowany). Osobno każdy byłby wykrywalny; razem dały 20 dni ciszy bez żadnego sygnału.


1. TL;DR

kb-mail-sync.timer tykał godzinowo od uruchomienia (2026-08-06 18:01, patrz docs/sessions/2026-08-06-kb-mail-sync-live.md) bez ani jednego kolejnego udanego zapisu koperty aż do 2026-08-26. Poller "żył" — proces startował, łączył się z IMAP, kursor w mail_sync_state się nie ruszał, bo save_eml dostawał PermissionError na każdej próbie zapisu. Kursor nie przeskoczył niezapisanej wiadomości — to zadziałało zgodnie z projektem (zero utraty poczty), ale oznaczało też, że failure był cichy: bez crasha, bez zmiany stanu widocznej z zewnątrz poza rosnącym opóźnieniem.

Powinien to złapać alert KbMailSyncStale (reguła w repo od 2026-08-06) — ale nie złapał, bo fleet-prometheus na VPS nigdy nie dostał /-/reload po dodaniu tego pliku reguł. Przez cały czas żyła wyłącznie grupa fleet-liveness sprzed czerwca. Wykryto ręcznie, przy sanity checku po 3 tygodniach przerwy operatorskiej — nie przez monitoring.


2. Root cause #1 — uprawnienia katalogów archiwum

~/kb/mail/archive/{gmail,fastmail}/2026/08/ powstały jako root:root — utworzone przez ręczne sudo-runy z 2026-08-06 (pierwsze uruchomienia przed włączeniem timera, patrz runbook mail-sync-run.md, sekcja o sudo do odczytu .env). Timer uruchamia job jako User=oskar (po zrzuceniu uprawnień z jednostki systemd) — save_eml w packages/kb-mail dostawał PermissionError przy próbie zapisu .eml do katalogu należącego do roota, na każdym z 480 ticków od 2026-08-06 18:01 do naprawy.

Design "kursor nie przeskakuje niezapisanej wiadomości" (pkg-kb-mail.md) zadziałał dokładnie tak jak zamierzono: zero utraty poczty, folder stał w miejscu zamiast przeskoczyć błąd i zostawić dziurę w korpusie. Cena tego bezpieczeństwa: bez monitoringu wygląda identycznie jak "brak nowej poczty" — legalny stan mailboxa, którego runbook świadomie nie traktuje jako alarmowy (patrz decyzja w kb-mail-sync.yml o odrzuceniu progu na "brak nowej poczty").

3. Root cause #2 — dlaczego 20 dni ciszy, nie 6 godzin

Reguła KbMailSyncStale (próg 6h, for: 5m) i kb-ingest.yml leżały w repo od 2026-08-06. Scrape config PIHA (fleet-node/piha100.108.208.3:9100) był poprawny od czerwca — target node_exportera PIHA już był zdefiniowany. Ale fleet-prometheus na VPS nigdy nie dostał POST /-/reload po tym, jak te dwa pliki reguł wylądowały w bind mouncie rules/. W runtime żyła wyłącznie grupa fleet-liveness, załadowana przy ostatnim restarcie kontenera (czerwiec). Alert KbMailSyncStale nie istniał operacyjnie — mógł mieć idealną logikę i wciąż nic by nie wysłał, bo Prometheus nie wiedział o jego istnieniu.

Dwa błędy nałożyły się: gdyby monitoring żył, alert wystrzeliłby po ~6h od pierwszego PermissionError (2026-08-07 ok. 00:00), nie po 20 dniach. Gdyby zapis działał, martwy monitoring nie miałby czego przegapić.


4. Naprawa

Zapis (2026-08-26, przez CC z SOLARII, worktree recon+fix na PIHA):

  1. chown -R oskar:oskar ~/kb/mail/archive/{gmail,fastmail}/2026/08 na PIHA.
  2. Ręczny tick #1: 170 inserted / 379 perm-errors — perm-errors to zaległe próby sprzed chowna w tej samej partii (kolejka nie czyściła się między nieudanymi próbami).
  3. Ręczny tick #2: 378 inserted / 155 archive_exists, errors=0 — pełne odzyskanie, kursor dogonił IMAP. Łącznie 548 zaległych kopert wchłonięte bez strat.

Monitoring (2026-08-26, przez CC z SOLARII, SSH na VPS):

  1. promtool check rules na kb-mail-sync.yml w kontenerze fleet-prometheus — OK, plik był już poprawny składniowo (potwierdzenie, że problem był czysto operacyjny, nie błąd w regule).
  2. POST http://100.95.58.48:9090/-/reload — 200, /api/v1/rules od razu pokazał wszystkie trzy grupy: fleet-liveness, kb-ingest, kb-mail-sync.
  3. Weryfikacja łańcucha end-to-end:
    • kb_mail_sync_last_success_timestamp{node="piha"} — scrape'owana, świeża (~5,8 min).
    • /api/v1/alerts — pusto; reguła KbMailSyncStale w stanie inactive/health: ok (nigdy nie zdążyła nic zaalarmować na starych danych — pierwsza ewaluacja po reloadzie zastała już świeżą metrykę).
    • brain-watchdog @ PIHA — PROMETHEUS_URL w .env poprawnie wskazuje na http://100.95.58.48:9090, kontener healthy od 2 tygodni, brak wpisów o poll-failure w logach → droga do Telegrama wpięta.

Żadna zmiana w repo nie była potrzebna dla naprawy monitoringu — config rules/*.yml i scrape_configs były poprawne od dawna, brakowało wyłącznie /-/reload w runtime.


5. Rekomendacje

R1 (proces, do CC) — utwardzić własność katalogów po ręcznym sudo-runie. Po każdym ręcznym uruchomieniu mail-imap-sync przez sudo (np. do odczytu .env 600 root:root, patrz runbook): albo chown -R oskar:oskar na archive/ od razu po runie, albo wariant sudo zachowujący ownera docelowych katalogów. Docelowo save_eml/definicja jednostki systemd powinny wymuszać poprawnego ownera tworzonych katalogów niezależnie od tego, kto je stworzył (np. umask/chown przy starcie joba, albo katalogi tworzone z wyprzedzeniem z właściwymi uprawnieniami zamiast mkdir -p przy pierwszym zapisie).

R2 (proces, do CC) — dopisać /-/reload do procedury deployu reguł Prometheusa. Dodanie pliku reguł do services/fleet-prometheus/rules/ bez reloadu jest w tej chwili niewidoczne — git diff pokazuje zmianę, deploy przechodzi zielono, a Prometheus i tak jej nie widzi aż do następnego restartu kontenera. Brakujący krok reload trzeba dopisać do runbooka deployu fleet-prometheus (do zlokalizowania/utworzenia: fleet-prometheus-deploy.md), najlepiej jako automatyczny krok w scripts/deploy/deploy.sh dla tego serwisu, nie tylko jako notatka w dokumentacji.

R3 (bezpieczeństwo, pilne) — rotacja sekretów po wycieku w transkrypcie. TG_TOKEN brain-watchdoga wypłynął w outpucie sesji 2026-08-26 (cat .env przez SSH w celach diagnostycznych) — czwarty udokumentowany przypadek tej klasy (poprzedni: hasło kb-postgres, 2026-08-05/06, patrz docs/sessions/2026-08-06-kb-mail-sync-live.md sekcja "Otwarte"). Do rotacji łącznie: TG_TOKEN (brain-watchdog) + hasło kb-postgres (WISI) — oba są zaległe z poprzednich incydentów tej samej klasy i warto zrobić je w jednej sesji zamiast kolejnych osobnych.


6. Nie w zakresie tej naprawy (follow-upy niezmienione z 2026-08-06)

Bez zmian względem stanu z poprzedniej sesji — patrz docs/sessions/2026-08-06-kb-mail-sync-live.md "Otwarte":

  • Fix UID SEARCH ALL na duże foldery (crash na kontach >_MAXLINE).
  • Załączniki PDF z maili.
  • Charset/mojibake w co najmniej 12 kopertach.
  • NUL w nagłówkach jsonb (gmail-header-backfill/bulk-import).

7. Stan końcowy

  • Kursor mail_sync_state dogonił IMAP na obu kontach, 548 zaległych kopert wchłonięte, 0 błędów w ostatnim ticku.
  • fleet-prometheus ma żywe wszystkie trzy grupy reguł; KbMailSyncStale faktycznie monitoruje po raz pierwszy od wdrożenia 2026-08-06.
  • Łańcuch metryka → reguła → alert → brain-watchdog → Telegram zweryfikowany end-to-end, bez dziur.
  • Otwarte: R1R3 powyżej (poza zakresem tej sesji, do osobnych tasków).