--- okf: "0.1" type: incident visibility: private status: active updated: 2026-08-26 links: - ../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](../../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](../runbooks/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](../services/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](../../services/fleet-prometheus/rules/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`/`piha` → `100.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](../../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](../../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: R1–R3 powyżej (poza zakresem tej sesji, do osobnych tasków).