# Operationeel runbook ## Dagelijkse controle Controleer bij voorkeur automatisch en minstens wekelijks handmatig: - `/health/ready/` is 200; - web, worker, scheduler, PostgreSQL en Redis zijn actief; - vrije ruimte op database-, media-, log- en back-upvolume; - bronnen in `quarantined`, `paused` of met oplopende `failure_count`; - `DigestOutbox` met status `failed` of langdurig `pending`; - meest recente geslaagde back-up en laatste hersteltest; - onverwachte login- of policyfouten in logs. ## Incidentprioriteiten | Niveau | Voorbeeld | Eerste actie | |---|---|---| | P1 | datalek, secret gepubliceerd, ongeautoriseerde netwerkfetch | stop web/worker/scheduler, isoleer netwerk, roteer secrets | | P2 | database niet beschikbaar, imports corrupt, herhaalde mail | stop schrijvende processen, behoud bewijs, maak snapshot | | P3 | één adapter kapot, digest vertraagd, bron in quarantaine | pauzeer bron/functie, herstel met fixture en test | | P4 | copy/layout/probleem zonder datarisico | registreer backlogtaak en plan normale release | ## Web niet ready 1. Controleer liveness. Faalt die ook, inspecteer webcontainer/proces en poortbinding. 2. Bij `database=error:*`: controleer PostgreSQL-status, disk, credentials en netwerknaam. 3. Test vanuit webcontainer of DNS `postgres` resolveert en poort 5432 bereikbaar is. 4. Draai geen migraties herhaald blind; lees eerst de laatste stacktrace. 5. Herstart alleen de falende laag. Een volledige stackrestart kan diagnostiek wissen. ## Worker verwerkt niets 1. Controleer Redis en `CELERY_BROKER_URL`. 2. Controleer workerqueues `high,default,low`. 3. Inspecteer actieve en gereserveerde taken: ```bash celery -A config inspect ping celery -A config inspect active celery -A config inspect reserved ``` 4. Kijk naar timeouts, policyblokkades en retryloops. 5. Start niet meerdere schedulers; dat kan dubbele planning veroorzaken, ook al zijn imports idempotent. ## Bron faalt of gaat in quarantaine 1. Laat quarantaine staan; forceer geen fetch. 2. Lees alleen categorie, status, URL-host en beperkte foutmelding in `SourceRun`. 3. Controleer of het domein, redirectdoel, robots-/voorwaardenstatus of contenttype veranderde. 4. Download geen pagina met een onbeveiligde shellopdracht vanaf de productiehost. 5. Reproduceer met een gesaneerde fixture in een ontwikkelomgeving. 6. Werk parser/policy en securitytests bij. 7. Zet bron pas terug op `trial`, voer één begrensde run uit en promoveer daarna eventueel naar `active`. ## IMAP-import stopt 1. Pauzeer alleen de falende platformmailbox op de bronpagina; andere mailboxen blijven werken. 2. Controleer provider, publieke IMAP-host, poort 993, mailboxmap, account en app-wachtwoord. 3. Verifieer dat `MAILBOX_CREDENTIAL_KEYS` aanwezig is en dat bij een rotatie de oude sleutel nog achter de nieuwe staat. 4. Verifieer dat de mailbox niet is hernoemd en het account niet is vergrendeld. 5. Alle polls selecteren de mailbox read-only; een reeds verwerkte mail is per mailbox idempotent via message identity. 6. Controleer het gekozen interval en `next_poll_at`; de scheduler zoekt iedere vijf minuten naar verschuldigde koppelingen. 7. Controleer `IMAP_MAX_MESSAGES_PER_POLL` en `IMAP_MAX_MESSAGE_BYTES`. Vergroot die niet om een onverwacht groot of verdacht bericht blind te verwerken. 8. Controleer bij een leeg resultaat of het jobboardmailformaat nog gelabelde HTTPS-vacaturelinks op het gekozen platformdomein bevat; versoepel de domeinfilter niet zonder fixture en securitytest. 9. Activeer de mailbox opnieuw, kies **Nu synchroniseren** en controleer `last_success_at`. Verwijder geen `EmailMessageRecord` om zonder analyse te herhalen. ## Mailboxsleutel roteren 1. Maak een nieuwe Fernet-sleutel en zet die als eerste in `MAILBOX_CREDENTIAL_KEYS`; behoud de oude sleutel erachter. 2. Herstart web, worker en scheduler zodat alle processen dezelfde sleutellijst gebruiken. 3. Draai `python manage.py rotate_mailbox_credentials`. 4. Synchroniseer één mailbox en controleer dat geen ciphertext, app-wachtwoord of volledige mailpayload in logs staat. 5. Verwijder daarna de oude sleutel en herstart opnieuw. ## Digest ontbreekt of dubbel - Controleer actief profiel, digesttijd/zone en `DIGEST_RECIPIENT`. - Controleer `DigestOutbox` op datum en status. - Bij SMTP-fout: corrigeer configuratie en retry dezelfde outbox gecontroleerd. - Maak geen tweede outboxrecord handmatig; uniciteit per profiel/datum is de duplicaatbarrière. - Een lege digest is toegestaan wanneer geen vacature boven de ingestelde drempel valt. ## Verdachte of kwaadaardige vacature-inhoud 1. Open de originele bron niet automatisch in een ingelogde browser. 2. Zet bij twijfel het raw document op `quarantined` zodat retentie het bewijs niet wist. 3. Controleer opgeslagen gesaniteerde tekst en veldherkomst. 4. Voeg de payload als geanonimiseerde fixture toe wanneer een parser/sanitizerfout bestaat. 5. Laat AI-output nooit een commando, URL-fetch of configuratiemutatie veroorzaken. ## Sollicitatiedossier exporteren en opschonen 1. Open de sollicitatielijst in een ingelogde gebruiker-sessie en ga naar het dossier. 2. Controleer dat de timeline alleen mutaties voor dat dossier bevat (status, notities, contact, snapshot). 3. Exporteer met het dossier-exportendpoint om een zip met `application.json`, `timeline.csv`, `application_print.html` te controleren. 4. Controleer het zipbestand lokaal op: - correcte dossier-ID; - geen raw e-mailbody of bron-payload; - geen externe assets of scriptrequests in HTML. 5. Verwijderen mag alleen op gebruikersniveau (`delete` endpoint); herhaalde delete-aanroepen moeten zonder fout uitkomen. ## Schijfruimte laag 1. Stop scheduler en worker wanneer de database- of PostgreSQL-volume kritiek vol is. 2. Controleer oude back-ups, containerlogs, raw-documentretentie en Ollama-modellen. 3. Verwijder nooit willekeurig PostgreSQL-bestanden. 4. Laat `cleanup_raw_documents` alleen niet-gequarantaineerde verlopen documenten opruimen. 5. Vergroot volume of herstel rotatie, start daarna database, web, worker en scheduler in die volgorde. ## Secret vermoedelijk gelekt 1. Stop externe toegang en disable betrokken account/token. 2. Roteer Django secret, adminwachtwoord, database-, SMTP-, IMAP- en registrycredentials voor zover betrokken. 3. Trek oude sessies in door sessies te verwijderen of het secret te wijzigen. 4. Controleer logs en source-runs op misbruik, zonder gevoelige payload verder te kopiëren. 5. Maak een incidentnotitie met exacte tijden, scope, rotaties en herstelvalidatie. ## Herstelvalidatie Na ieder incident: ```bash python manage.py check python manage.py migrate --plan python manage.py shell -c "from apps.core.health import readiness; print(readiness())" ``` Controleer daarna login, één fixture-import, scoreberekening, bronlijst, sollicitatielijst en mailbackend. Start live polling pas nadat deze controles slagen.