Playbook: Pinbox24 “No Logs” P1 — REAL outage (bms-1 mezmo-agent 403 key mismatch)
Trigger: P1 GitHub issue titled [P1] Pinbox24 no logs - bms-1 (auto-generated by the
mezmo-alert-router n8n workflow, Sc2AKCdslinnSvWs). Backed by the Prometheus alert
Pinbox24NoLogs (monitoring/prometheus/rules/pinbox24.yml, per-backend
pinbox24_backend_log_lines_5m_total{container=...} == 0, for: 15m, severity warning — #5605
generalised it from the v42-prod-only unlabeled gauge to cover W3/v32-prod too) and the Mezmo
absence alert on pm2_v42-prod from bms-1.
Host of record: bms-1 (94.23.26.113, OVH hostname ns367522) — Pinbox24 production.
Time budget: P1 — resolve within 1h.
First decide false-positive vs. real outage. Most of these alerts fire spuriously (storm tracked in #1310 / PR #1379). Run the Mezmo confirmation query in
pinbox24-no-logs-false-positive.mdfirst. Use THIS playbook only once that query confirms nopm2_v42-prodlines from bms-1 in the last 2h — a genuine log-forwarding outage. The concrete root cause documented here was identified in #1425 (2026-06-28).
1. Root cause (#1425)
Pinbox24 application containers on bms-1 were healthy and running (docker ps showed
v32-prod, v42-prod, etc. all UP). The break was purely in log forwarding: the
mezmo-agent Docker container on bms-1 was failing to ship logs with:
403 Forbidden: "Account not found"
403 "Account not found" from the Mezmo ingestion endpoint means the ingestion key the agent is
sending is no longer valid for the account — i.e. the agent’s key has drifted from the live key.
Confirmed by comparing (SHA-256, value never printed):
LOGDNA_AGENT_KEYin/opt/mezmo-agent/.envon bms-1, againstMEZMO_INGESTION_KEYinsecrets/monitoring.env.sops(the source of truth)
The two hashes did not match → the agent was authenticating with a stale ingestion key.
Why it drifted (systemic cause — fix this to stop recurrence)
secrets-sync.yml auto-deploys SOPS values only to vps-i1 and bms-4. bms-1 is NOT on the
auto-sync path (it is also unreachable from bms-4 / the AI runners — no agent SSH key; see #1270).
So when MEZMO_INGESTION_KEY was last rotated in SOPS, the new value was never propagated to
/opt/mezmo-agent/.env on bms-1, and the agent kept sending the revoked key until Mezmo rejected
it with 403.
2. Who can run the fix
The remediation requires SSH to bms-1 as root. Update (#5719, 2026-08-06): the bms-4 infra-task
worker now holds the bms-1 root key and can run these steps directly — bms-1 is Pinbox24
production and the infra-task worker carries the stack SSH grant (see
docs/w3-w4-stack-operations.md). The old “bms-4 / AI-Dev runners
cannot reach bms-1 (Permission denied (publickey), #1270)” claim is stale. The human
workstation (C:\Users\konar\.ssh\id_ed25519) still works too; either host that can both decrypt
SOPS and SSH bms-1 can perform the key-sync fix below.
Secret safety: the ingestion key is a credential. Never echo it, never paste it into a chat, commit, or issue comment. Reference the key NAME (
MEZMO_INGESTION_KEY/LOGDNA_AGENT_KEY) only. Use the read-into-variable pattern below so the value never lands in stdout or shell history.
3. Fix — sync the ingestion key and restart the agent
Run from a host that can both decrypt SOPS and SSH bms-1 (human workstation).
cd /opt/p24-infra # or your local repo checkout
export SOPS_AGE_KEY_FILE="$HOME/.age/p24-infra-keys.txt"
# 1. Read the live ingestion key silently into a variable — NEVER echo $MEZMO_KEY
MEZMO_KEY=$(sops --decrypt --input-type dotenv --output-type dotenv \
secrets/monitoring.env.sops | grep '^MEZMO_INGESTION_KEY=' | cut -d= -f2-)
[ -n "$MEZMO_KEY" ] && echo "key loaded" || { echo "MEZMO_INGESTION_KEY missing in SOPS"; exit 1; }
# 2. Push it to bms-1's mezmo-agent .env via stdin (value never appears in argv / ps / history).
# IMPORTANT: docker-compose v1.25.3 on bms-1 includes trailing \n from env_file values,
# producing a 33-char key instead of 32 → 403 "Account not found" from Mezmo.
# Write WITHOUT a trailing newline (printf without \n at end).
printf '%s' "$MEZMO_KEY" | ssh root@94.23.26.113 \
'KEY=$(cat | tr -d "\n\r")
printf "LOGDNA_AGENT_KEY=%s" "$KEY" > /opt/mezmo-agent/.env
echo "updated ($(wc -c < /opt/mezmo-agent/.env) bytes — expect 49)"'
unset MEZMO_KEY # 3. Clear immediately
# 4. Restart the agent to pick up the new key (bms-1 uses docker-compose v1, NOT docker compose)
ssh root@94.23.26.113 \
'cd /opt/mezmo-agent && docker-compose up -d --force-recreate mezmo-agent'If the compose service name differs, discover it first:
ssh root@94.23.26.113 'cd /opt/mezmo-agent && docker compose config --services'.
4. Verify
# a) Agent container is up and the 403 is gone (filter out any secret-bearing lines)
ssh root@94.23.26.113 \
'cd /opt/mezmo-agent && docker compose ps && \
docker compose logs --tail=40 mezmo-agent 2>&1 | grep -iE "403|forbidden|account not found|error|connected|ingest" || true'
# Expect: container "Up"; NO "403 / Account not found"; ideally a "connected"/ingest-OK line.
# b) Pinbox24 app containers still healthy (no app impact from the agent restart)
ssh root@94.23.26.113 "docker ps --format '{{.Names}}\t{{.Status}}' | grep -i prod"Then confirm logs are actually arriving in Mezmo (authoritative check — run from bms-4/vps-i1):
use the /v2/export query in
pinbox24-no-logs-false-positive.md §Step 1. Recent
pm2_v42-prod lines (age < ~30 min) = forwarding restored.
When confirmed, post a resolution comment with the evidence and close the issue:
gh issue comment 1425 --repo radieu/p24-infra --body-file resolution.md
gh issue close 1425 --repo radieu/p24-infra --reason completed5. Prevention
The durable fix is to keep bms-1’s mezmo-agent key in step with SOPS, since bms-1 is off the
secrets-sync.yml auto-deploy path:
- Add bms-1 to the
MEZMO_INGESTION_KEYrotation distribution checklist. Indocs/secrets-rotation-log.mdand the rotation procedure (CLAUDE.md §“When you rotate an existing infra secret”), record that rotatingMEZMO_INGESTION_KEYMUST also update/opt/mezmo-agent/.env(LOGDNA_AGENT_KEY) on bms-1 and restart the agent — it is not covered bysecrets-sync.yml. - Consider a periodic drift check (e.g. credential-exporter or a cron) that compares the SHA-256 of
bms-1’s
LOGDNA_AGENT_KEYagainst SOPSMEZMO_INGESTION_KEYand alerts on mismatch — this would have surfaced the drift before Mezmo rejected the key. - Separately,
Pinbox24NoLogscan still false-positive because v42-prod logs go via the Docker JSON driver rather than/var/log(tracked in #1339, the PM2 volume-mount fix). That is a distinct issue from this key drift and does not need an alert-rule change here — the rule is already tuned towarning/for: 2h.
6. Escalation
If the key sync + restart does not restore forwarding (still 403, or no lines in Mezmo after ~15 min):
- Verify the ingestion key itself is valid against the Mezmo API (it may have been revoked, not just
drifted) — see
docs/mezmo-operations.md §Troubleshootingand rotate per its §5 if revoked. - Check Mezmo ingestion quota / plan limits.
- Per the Error Notification Standard, send a Discord alert via
P24_DISCORD_INFRA_SCRIPTS_ERRORS_WEBHOOK_URL(red) and keep the GH issue open with labelhuman-action.
References
pinbox24-no-logs-false-positive.md— false-positive triage- the authoritative Mezmo
/v2/exportconfirmation query (always run first)
- the authoritative Mezmo
docs/mezmo-operations.md— Mezmo agent config, key inventory, troubleshooting, rotation (§5)docs/n8n-operations.md§mezmo-alert-router— alert → Discord/GH routingmonitoring/prometheus/rules/pinbox24.yml—Pinbox24NoLogsalert definition- #1425 — this incident (mezmo-agent 403 key mismatch, root cause 2026-06-28)
- #1310 / PR #1379 — no-logs alert storm + dedup fix
- #1339 — v42-prod Docker-JSON-driver log forwarding (PM2 volume mount) — separate root cause
- #1270 — bms-1 unreachable from bms-4 / AI runners (no agent SSH key)
Audit Log — Log to infra_operations
After this operation completes, log it to the infra_operations audit table.
Python (Linux server — bms-4, vps-i1, vps-h1, or similar):
import sys
sys.path.insert(0, '/opt/p24-infra')
from scripts.lib.log_op import log_op
log_op(
actor="claude", # "radieu" for manual human ops, "claude" for agent
op_type="config_change",
resource="pinbox24-bms1",
result="success", # "success" | "failed" | "skipped"
detail="Pinbox24 no-logs issue resolved — logging config fixed on bms-1",
env="bms-1",
gh_issue=2730,
)PowerShell (Windows dev machine):
$env:SUPABASE_URL = (Get-Content "C:\code_2026\p24-infra\.env.local" | Select-String "^SUPABASE_URL=").ToString().Split("=",2)[1].Trim()
$env:SUPABASE_SERVICE_KEY = (Get-Content "C:\code_2026\p24-infra\.env.local" | Select-String "^SUPABASE_SERVICE_KEY=").ToString().Split("=",2)[1].Trim()
python -c "
import os, sys
sys.path.insert(0, 'C:/code_2026/p24-infra')
from scripts.lib.log_op import log_op
log_op('claude', 'config_change', 'pinbox24-bms1', 'success', 'Pinbox24 no-logs issue resolved — logging config fixed on bms-1', 'bms-1')
"
$env:SUPABASE_URL = ''; $env:SUPABASE_SERVICE_KEY = ''