P24-Infra Health Check — Design & Operations

Status: Design (proposed) Scope: All p24-infra elements — servers, containers, SaaS, CI/CD, data integrity Related: docs/nightly-checks-triage.md, docs/alert-response-runbook.md


1. Principles

  1. Check frequency matches failure impact. A WAHA session drop at 02:00 costs more than a doc compliance gap — check it every 2 hours, not daily.
  2. One label, one queue. All machine-generated issues get p24-infra-nc-alert. The hourly triage is the single consumer; no parallel alert paths that create duplicate noise.
  3. Three-tier response. Auto-fix (safe, reversible, 0-human) → AI-Dev worker (code/config change, no data risk) → Human escalation (everything else). The hourly triage classifies and routes.
  4. Transition-only notifications. Discord and Telegram fire on state change (OK→FAIL, FAIL→OK), not on every run. Repeat-fire is suppressed by checking if a matching open issue already exists.
  5. AI-Dev workers are opt-in per issue type. Only the explicitly whitelisted issue patterns spawn an AI-Dev worker. Unknown patterns always escalate to human.

2. Elements Inventory & Check Method

2.1 Servers

ElementHowToolChecks
vps-i1 SSH reachabilityssh -o BatchMode=yes -o ConnectTimeout=15nightly-infra-checkexits 0 = up
vps-h1 SSH reachabilitysamenightly-infra-check
bms-2 SSH reachabilitysamenightly-infra-check
bms-3 SSH reachabilitysamenightly-infra-check
bms-4 SSH reachabilitysamenightly-infra-check
vps-i1 CPU / RAM / networknode_exporter → Prometheus → AlertmanagercontinuousHighCPU alert at 85%
vps-h1 CPU / RAM / networknode_exporter → Prometheus → Alertmanagercontinuoussame
All servers diskdf -h over SSHnightly-infra-check≥85% → P2; ≥95% → P1
OOM killgrep -i oom /var/log/syslog over SSHnightly-infra-checkany match → P1
bms-1 diskdf -h over SSHnightly-infra-check≥85% → P2; ≥95% → P1

2.2 Docker Containers

Applies to: vps-i1 (monitoring stack), vps-h1 (WAHA + Traefik), bms-3 (staging + traccar), bms-4 (n8n + Traefik).

CheckMethodToolThreshold
Container statedocker ps --format '{{json .}}' over SSHnightly-infra-checkstate ∉ {running} → P2
Restart count spikecompare RestartCount to previousnightly-infra-check+3 in 24h → P2
CPU / memory per containercAdvisor → Prometheuscontinuousalert rules in monitoring/alerts/

Expected running containers per host:

vps-i1: caddy, prometheus, thanos-sidecar, thanos-query, grafana, grafana-image-renderer, alertmanager, loki, promtail, blackbox-exporter, uptime-kuma, queue-exporter, cost-exporter, pg-stats-exporter, backup-exporter, gotenberg, pdf-service, traccar, traccar-db, node_exporter, cadvisor

vps-h1: traefik, waha, node_exporter, cadvisor, promtail

bms-3: nginx-proxy, Pinbox24 staging containers, traccar, mongodb

bms-4: traefik, n8n (main + 3 workers), redis, node_exporter, cadvisor

2.3 Critical Business Services

These require application-level checks beyond Docker state.

ServiceCheckEndpoint / MethodPasses whenFrequency
Traccar (GPS)HTTP GETtraccar.vps-i1…HTTP 2xx/3xxevery 2h
WAHA (WhatsApp)HTTP GET server status/api/server/status + X-Api-KeyHTTP 200every 2h
WAHA sessionHTTP GET session/api/sessions/default → parse .status"status":"WORKING"every 2h
n8n (bms-4)HTTP GET health/healthzHTTP 200every 2h
n8n execution errorsREST API/api/v1/executions?status=error&limit=25 → filter last 24h0 errorsdaily 00:30
GrafanaHTTP GET health/api/healthHTTP 200every 2h
Infisical CEDECOMMISSIONED 2026-06-21 — no health check neededN/AN/A

WAHA note: /api/server/status returns 200 even when the session is FAILED (proven 2026-05-23 blackout). Must check both the server AND the session object.

2.4 Monitoring Stack

The monitoring stack is both a subject of health checks and the platform that performs some of them. This creates a partial self-monitoring loop.

ElementCheckMethodPasses when
Prometheus scrape targetsGET /api/v1/targetsnightly-infra-checkall health=up
Prometheus firing alertsGET /api/v1/alertsnightly-infra-check0 state=firing
Alertmanager livenessHTTPPrometheus blackboxHTTP 200
Grafana livenessHTTP /api/healthhealth-check.ymlHTTP 200
Thanos queryPromQL thanos_query_store_apis_dns_failures_totalPrometheus alert0
Backup exporter (Wasabi freshness)PromQL backup_age_secondsnightly-infra-checkall < 90000s (25h)
Log shipping (promtail → Loki)Prometheus metric promtail_sent_bytes_totalPrometheus alertincreasing

2.5 Database & Storage

ElementCheckMethodPasses when
MongoDB rs0 statemongosh --eval 'JSON.stringify(rs.status())' via SSH to bms-2nightly-infra-checkexactly 1 PRIMARY; all members ∈ {PRIMARY, SECONDARY, ARBITER}
MongoDB replication lagrs.status().members[].optimeDate diffnightly-infra-checklag < 30s
Supabase RESTGET /rest/v1/agent_sessions?select=count&limit=0health-check.ymlHTTP 200
Wasabi backup freshnessbackup_age_seconds metric (backup-exporter)nightly-infra-check< 25h per backup slot
Supabase audit action failuresGET /rest/v1/actions?last_run_status=eq.failed&…nightly-infra-check0 rows

2.6 CI/CD — GitHub Actions Runners

RunnerCheckMethodPasses when
ionos (et-oper)systemctl is-active actions.runner.radieu-et-operational-platform.ionos via SSHhealth-check.ymloutput = active
kdp-ionossystemctl is-active actions.runner.radieu-amazon-kdp-tango.kdp-ionos-runner via SSHhealth-check.ymloutput = active
hstgrpgrep -c -f Runner.Listener via SSHhealth-check.ymlcount ≥ 1
p24-infra-nightly croncheck last run timestamp in /var/log/p24-infra-nightly.lognightly-infra-checkran within last 26h

2.7 Compliance & Governance

CheckMethodToolThreshold
Docs compliance gapsSupabase dev_r_services?compliance_workbook=eq.no&status=eq.activenightly-infra-check0 rows = clean; any rows → P3 issue per gap
Credential rotation overduecredential-exporter → Prometheus → Alertmanagercontinuousauto_rotate=true → auto-trigger credential-rotation.yml; auto_rotate=false → human
EU AI Act registry gapsSupabase dev_r_ai_systems rows without last_reviewed within 30dnightly-infra-check0 rows
Audit engine action failuresSupabase audit.actions?last_run_status=eq.failednightly-infra-check0 rows

3. Check Frequency Matrix

Every 15s  ──► Prometheus scrape (node_exporter, cAdvisor, custom exporters)
               → fires Alertmanager rules → email P1 / daily digest P2

Every 2h   ──► health-check.yml (GH Actions, ionos runner)
               Traccar · Grafana · n8n · WAHA (server + session) · Supabase · 3 runners
               → Discord embed on state change · GH issue on failure

Every 1h   ──► hourly-devops-triage (bms-4 Linux cron)
               Reads p24-infra-nc-alert issues → auto-fix OR ai-dev-spawn OR human escalation
               → Telegram on human-required (quiet 22:00–07:00 Warsaw)

Daily 00:30 ─► nightly-infra-check (GH Actions, ionos runner)
               SSH deep checks: Docker state · MongoDB rs0 · disk · Prometheus targets
               · Prometheus alerts · n8n execution errors · Wasabi backup freshness
               · docs compliance · audit action failures
               → creates p24-infra-nc-alert issues for each problem found

Daily 23:00 ─► p24-infra-nightly (vps-i1 cron, claude-runner)
               /process-issues on dev branch — triages + implements Design-ready issues

Weekly Mon  ─► credential-rotation.yml
               Checks overdue credentials via credential-exporter metrics
               → auto-rotates auto_rotate=true credentials
               → human-action issue + Telegram for auto_rotate=false

4. Issue Reporting

Every machine-generated problem becomes a GitHub issue in radieu/p24-infra.

4.1 Severity levels

LevelCriteriaResponse SLA
P1-CriticalService completely down for business operations (WAHA session FAILED, MongoDB no PRIMARY, bms-1 disk ≥95%, OOM kill, Supabase unreachable)1 hour
P2-WarningDegraded / at-risk but not fully down (disk 85–95%, container restarting, n8n execution errors, backup stale, scrape target down)8 hours
P3-InfoNon-urgent gap (docs compliance missing, P3 audit action failure, AI Act registry stale)72 hours

4.2 Issue format

Title:  [nightly-check] {Component}: {short description}
Labels: bug | human-action | patch   +   p24-infra-nc-alert
Milestone: Triage
Body:
  ## Problem detected by nightly infra check

  **Component:** {component}
  **Severity:** P1-Critical | P2-Warning | P3-Info
  **Detected at:** {UTC timestamp}

  ### What was found
  {description}

  ### Raw output

{relevant snippet — no secret values}


### Suggested action
{what the agent recommends}

4.3 Notification routing per severity

SeverityDiscordTelegramGH Issue label
P1-CriticalImmediate red embedImmediate (if 07:00–22:00 Warsaw)p24-infra-nc-alert + human-action
P2-WarningNext hourly triage summary if unresolvedOnly if unresolved after 4hp24-infra-nc-alert + bug
P3-InfoNoneNonep24-infra-nc-alert + patch
Clean runNoneNoneComment on [nightly-health-status] issue

4.4 Deduplication rule

Before creating a new issue the agent searches for an open issue with the same [component] title fragment and label p24-infra-nc-alert. If one exists, it adds a comment instead of creating a duplicate. Dedup key: (alertname, server-label, component)not fingerprint-based (avoid the 2026-06-08 duplicate storm).


5. Reaction Model — Three Tiers

The hourly triage agent is the single routing brain. It reads all open p24-infra-nc-alert issues and decides the tier.

p24-infra-nc-alert issue
         │
         ▼
  ┌─ Tier 1: Auto-fix ──────────────────────────────────────────┐
  │  Safe, reversible, known exact action                        │
  │  Examples: docker restart (container not Dead), Prometheus   │
  │  config reload, label patch for doc gap                      │
  │  Action: SSH → fix → verify → comment "Auto-fixed" + move   │
  │  issue to In Progress                                        │
  └──────────────────────────────────────────────────────────────┘
         │ not auto-fixable
         ▼
  ┌─ Tier 2: AI-Dev worker ─────────────────────────────────────┐
  │  Requires code/config change, no data-loss risk, no prod    │
  │  auth change, no secret handling                             │
  │  Examples: missing workbook doc, n8n workflow config error,  │
  │  failed audit action (workbook spec update), Prometheus     │
  │  rule misconfiguration, broken GH Actions workflow          │
  │  Action: label ai-dev-queued → SSH to AI-Dev node →         │
  │  spawn worker-issue.md → worker opens PR → issue In Progress│
  └──────────────────────────────────────────────────────────────┘
         │ not ai-dev-solvable
         ▼
  ┌─ Tier 3: Human escalation ──────────────────────────────────┐
  │  Always: MongoDB, data loss risk, credential rotation,       │
  │  production gateway restart, disk ≥95%, OOM, bms-1 disk,   │
  │  P1-Critical, unknown pattern                                │
  │  Action: label human-action → Telegram → Discord            │
  └──────────────────────────────────────────────────────────────┘

5.1 Auto-fix patterns (Tier 1)

Issue title patternActionSafety check
Container '…' is in state: Exited|Restartingdocker restart {container} on the labeled server; re-check state after 10sOnly if not Dead; not on bms-1 (production Pinbox24)
Prometheus.*config reloadcurl -X POST http://localhost:9090/-/reloadCheck HTTP 200 response
Docs complianceLabel patch, comment with link to documentation standardNo SSH needed
rotation.*reminder (auto_rotate=true)Trigger credential-rotation.yml via gh workflow runOnly for flagged auto_rotate=true credentials

5.2 AI-Dev worker patterns (Tier 2)

Issue title patternSpawned onWorker task
Docs compliance.*missing workbookAI-Dev-IO1 (vps-i1)Create docs/{service}-operations.md, update dev_r_services
Audit:.*failedAI-Dev-IO1Investigate audit action, update workbook spec
GH Actions.*failedAI-Dev-IO1Read workflow, diagnose, fix, PR
Prometheus.*target.*DOWN (not infrastructure)AI-Dev-IO1Check probe config, fix endpoint path, PR
n8n.*execution error (known workflow)AI-Dev-BMS4-1 (bms-4)Read n8n execution log, fix workflow node, activate

How the worker is spawned:

# On the appropriate AI-Dev node (via SSH from the triage agent)
ssh claude-runner@217.154.82.162 \
  "cd /home/claude-runner/p24-infra && \
   claude --dangerously-skip-permissions -p '/worker-issue #{issue_number}'" &

Safety gates — AI-Dev workers NEVER auto-trigger when:

  • Issue contains: MongoDB, P1-Critical, disk 9x%, secret rotation, production restart, rs0, quorum, data loss, OOM
  • More than 2 AI-Dev workers already running (check agent_sessions table)
  • Issue is labeled human-action (already escalated)

Worker SLA: 4 hours. If the spawned worker doesn’t close the issue or open a PR within 4 hours, the next triage run escalates to Tier 3 and sends Telegram.

5.3 Human escalation patterns (Tier 3 — always)

  • MongoDB rs0 state change (any member)
  • P1-Critical severity in body
  • Disk ≥ 90% on any server
  • OOM kill detected
  • WAHA session FAILED (after auto-restart attempt fails)
  • bms-1 (Pinbox24 production) any issue
  • Secret / credential rotation (auto_rotate=false)
  • Production service requiring downtime
  • Unknown issue pattern (no match in Tier 1 or Tier 2)

6. Current Implementation vs. This Design

What’s already implemented

ComponentStatus
health-check.yml — HTTP + systemctl every 2h✅ Live
nightly-infra-check.yml — Claude agent 00:30 UTC✅ Live (GH Actions)
hourly-devops-triage — bms-4 Linux cron✅ Live
p24-infra-nightly — 23:00 UTC process-issues✅ Live
Prometheus + Alertmanager continuous alerts✅ Live
Discord on health-check failure✅ Live
Telegram on human-required triage✅ Live
Dedup by (alertname, instance, credential)✅ Fixed 2026-06-08

What’s missing / gaps

GapPriorityStatusNote
Tier 2: AI-Dev worker spawning in hourly triageP2DoneAdded $AI_DEV_PATTERNS, Invoke-AIDevWorker, SLA watchdog to hourly-devops-triage.md
Worker SLA watchdog (4h timeout → Telegram)P2DonePhase 1 of triage now checks ai-dev-queued issues older than 4h
bms-1 disk/Docker/OOM checkP1DoneAdded bms-1 (root@94.23.26.113) to $SERVERS in nightly-infra-check.md; disk at 75%
P1-Critical bypasses Telegram quiet hoursP1DoneSend-TelegramAlert now accepts $severity; P1 skips the quiet-hours guard
health-check.yml IONOS runner blind spotDesign riskDoneSplit into health-http (hstgr runner) + health-runners (ionos), each self-reporting; no paid runners
WAHA auto-restart attempt before escalationP2Opendocker compose up -d waha if container state ≠ running; only escalate if still down
Transition-only Discord (fires on every failure, not only on state change)P3OpenSpec 06 improvement doc
bms-4 runner check in health-check.ymlP2OpenNo check on AI-Dev-BMS4-1 runner yet
Cert expiry checkP3Opendocs/improvements/12-cert-expiry-alerts.md
Nightly check: p24-infra-nightly last-run timestampP3OpenAdd to nightly check Phase list

7. Complete Check Ownership Table

ElementChecked byHowFrequencyCreates issue?
vps-i1 SSHnightly-infra-checkssh BatchMode00:30 UTCyes if fails
vps-h1 SSHnightly-infra-checkssh BatchMode00:30 UTCyes if fails
bms-2 SSHnightly-infra-checkssh BatchMode00:30 UTCyes if fails
bms-3 SSHnightly-infra-checkssh BatchMode00:30 UTCyes if fails
bms-4 SSHnightly-infra-checkssh BatchMode00:30 UTCyes if fails
vps-i1 disknightly-infra-checkdf -h via SSH00:30 UTC≥85% → P2, ≥95% → P1
vps-h1 disknightly-infra-checkdf -h via SSH00:30 UTCsame
bms-2 disknightly-infra-checkdf -h via SSH00:30 UTCsame
bms-3 disknightly-infra-checkdf -h via SSH00:30 UTCsame
bms-4 disknightly-infra-checkdf -h via SSH00:30 UTCsame
bms-1 disknightly-infra-checkdf -h via SSH00:30 UTC75% — monitored
OOM killsnightly-infra-checkgrep syslog via SSH00:30 UTCP1
vps-i1 CPU/RAMPrometheus + Alertmanagernode_exporter15sAlertmanager email
vps-h1 CPU/RAMPrometheus + Alertmanagernode_exporter15sAlertmanager email
Docker containers (vps-i1)nightly-infra-checkdocker ps JSON00:30 UTCstate anomaly → P2
Docker containers (vps-h1)nightly-infra-checkdocker ps JSON00:30 UTCsame
Docker containers (bms-3)nightly-infra-checkdocker ps JSON00:30 UTCsame
Docker containers (bms-4)nightly-infra-checkdocker ps JSON00:30 UTCsame
Traccar HTTPhealth-check.ymlcurlevery 2hGH issue + Discord
WAHA server statushealth-check.ymlcurl /api/server/statusevery 2hGH issue + Discord
WAHA session WORKINGhealth-check.ymlcurl /api/sessions/defaultevery 2hGH issue + Discord
Grafana /api/healthhealth-check.ymlcurlevery 2hGH issue + Discord
n8n /healthz (bms-4)health-check.ymlcurlevery 2hGH issue + Discord
Supabase RESThealth-check.ymlcurlevery 2hGH issue + Discord
GH runner ionos-ethealth-check.ymlsystemctl via SSHevery 2hGH issue + Discord
GH runner kdp-ionoshealth-check.ymlsystemctl via SSHevery 2hGH issue + Discord
GH runner hstgrhealth-check.ymlpgrep via SSHevery 2hGH issue + Discord
MongoDB rs0 statenightly-infra-checkmongosh rs.status()00:30 UTCany bad state → P1
MongoDB replication lagnightly-infra-checkoptimeDate diff00:30 UTC>30s → P2
Prometheus targetsnightly-infra-check/api/v1/targets00:30 UTCany down → P2
Prometheus firing alertsnightly-infra-check/api/v1/alerts00:30 UTCper alert severity
n8n execution errorsnightly-infra-checkn8n REST API00:30 UTCany errors → P2
Wasabi backup freshnessnightly-infra-checkbackup_age_seconds metric00:30 UTC>25h → P2
Infisical CE healthREMOVED — Infisical CE decommissioned 2026-06-21N/AN/AN/A
Docs compliancenightly-infra-checkSupabase dev_r_services00:30 UTCany gap → P3
Audit action failuresnightly-infra-checkSupabase audit.actions00:30 UTCany failed → P2
Credential rotationcredential-rotation.ymlcredential-exporterweekly Monauto or human-action
EU AI Act registrynightly-infra-checkSupabase dev_r_ai_systems00:30 UTCstale review → P3
p24-infra-nightly last runmissing — add to nightlytail /var/log/p24-infra-nightly.log00:30 UTCif >26h old → P2

8. Blind Spots & Risk Mitigations

RiskImpactMitigation
IONOS runner down → health-check.yml blindP1: WAHA/Traccar check fails silentlyMove HTTP-only steps to GitHub-hosted runner; keep SSH steps on ionos. Tracked in spec 06.
nightly-infra-check itself fails (Claude crash)P2: No issues created, false OKThe GH Actions failure step creates a meta-issue + Discord.
health-check.yml creates duplicate issues on every 2h failureNoiseAdd transition guard: only create new issue if no open server-down issue exists (already partially implemented; verify dedup covers all steps).
bms-1 not in nightly check targetsP1 missed: disk at 75% and risingFixed: added bms-1 to $SERVERS array in nightly-infra-check.md.
AI-Dev worker hangs or produces broken PRIn Progress issue stuckSLA watchdog (4h) added to hourly triage: if ai-dev-queued issue has no PR linked after 4h, escalate to Tier 3.
Telegram quiet hours (22:00–07:00 Warsaw) suppress P1Overnight P1 goes unnoticed until morningQuiet hours exempt P1-Critical — send regardless of hour. Only P2/P3 respect quiet hours.

9. Reference: Label & Milestone Semantics

LabelMeaning in health-check context
p24-infra-nc-alertCreated by an automated check — processed by hourly triage
human-actionTriage classified as Tier 3 — Telegram sent, waiting for human
ai-dev-queuedTriage classified as Tier 2 — AI-Dev worker spawned
bugP1 or P2 issue (fixable defect)
patchP3 issue (docs, compliance)
server-downCreated by health-check.yml on HTTP/systemctl failure
infra-check-failAdded to existing server-down issue on repeat failure
resolvedAdded when auto-closed (service recovered)
MilestonePipeline stage
TriageFreshly created — not yet assessed
In ProgressBeing worked (auto-fix or AI-Dev worker active)
ReviewPR open, awaiting review/merge