ntfy.vaspian.me
vaspian

vaspian-telescope

Telescope (Number Lab) operational events — test lifecycle, phone connectivity, voice-worker errors, and host/system health

Telescope (Number Lab) operational events — the carrier spam-label test rig. Covers the voice-test lifecycle (submitted → completed), Android handset connectivity on the rig, voice-worker exceptions, and host/system health. Mixed urgency: most traffic is low/default, but phones-down, worker errors, and host-health failures page at high/max.

ntfy.sh/vaspian-telescope · added 2026-05-22

Publishers

Every code path / config block that produces messages on this topic today. If you wire a new publisher, add it here.

Telescope API — test submitted + phone watcher
Telescope prod host (docker telescope-api) · number-lab/telescope: telescope/integrations/ntfy.py ← api/routes/test_voice.py + api/phone_watcher.py

The FastAPI process publishes two event classes through the shared ntfy helper (integrations/ntfy.py — a best-effort async wrapper that logs and swallows publish failures so a dead ntfy.sh never blocks a test). (1) Test submitted: one push per fired batch when a voice test is enqueued, deduped in Redis with SET NX (key ntfy:batch_submitted:<batch_id>, TTL 1h) so multi-CID submissions that share a batch fire once. (2) Phone connectivity: a background watcher polls the ADB handset snapshot every ≥15s and fires only on edges — connected→disconnected (offline) and disconnected→connected (back online). The first-ever observation seeds state silently; steady state stays quiet.

  • · Test submitted — voice batch enqueued (priority low, tags arrow_forward + telephone_receiver; Redis NX dedup, TTL 1h)
  • · Phone offline — handset drops off ADB, connected→disconnected edge (priority high, tags warning + iphone)
  • · Phone back online — handset reconnects, disconnected→connected edge (priority low, tags white_check_mark + iphone)
Telescope voice-worker — batch complete + worker errors
Telescope prod host (docker telescope-voice-worker-{verizon,att,tmobile}) · number-lab/telescope: telescope/worker/main_voice.py ← telescope/integrations/ntfy.py

Each per-carrier voice worker publishes two event classes. (1) Batch complete: once every test row in a batch reaches a terminal state, the worker aggregates clean / labeled / undelivered counts and a pass %, then fires once (Redis publish_once, key ntfy:batch_completed:<batch_id>, TTL 24h) — the same event the auto-report email rides on. Tone keys off the pass rate. (2) Worker error: on an unhandled job exception, fired once per (carrier, exception-class) with a 300s cooldown (key ntfy:worker_err:<carrier>:<ExcClass>) so a stuck dependency failing 50 calls in a row pages once, not 50×. publish_once drops the dedup key if the publish itself raises, so a transient ntfy.sh 5xx doesn't silently swallow the alert.

  • · Test complete, pass ≥ 80% — priority low, tags white_check_mark + telephone_receiver
  • · Test complete, pass 50–79% — priority default, tags warning + telephone_receiver
  • · Test complete, pass < 50% — priority high, tags x + telephone_receiver
  • · Worker error — unhandled job exception (priority high, tags rotating_light + robot; 300s cooldown per carrier + exception class)
sysmon.sh — host / system health
Telescope prod host (systemd timer, every 5 min) · number-lab/telescope: bin/sysmon.sh (topic read from /opt/telescope/.env → TELESCOPE_NTFY_TOPIC)

A shell health monitor that runs from a systemd timer every 5 minutes on the prod host and catches the things that quietly degrade between deploys: root-disk usage, memory pressure, the full expected docker container set (21 containers — api, dashboard, nginx, postgres, redis, minio, asterisk, cloudflared, adb-watchdog, plus the per-carrier voice/sms/poller/importer workers), ADB seeing all 3 handsets, and the inbound Cloudflare tunnel (a 200/302 from telescope.vaspian.me). State is written per alert key to /var/lib/telescope/sysmon.state; an alert fires only on the healthy→unhealthy edge and stays silent while the condition persists. Recovery (unhealthy→healthy) fires once at priority 2. Every title is prefixed 'telescope · '.

  • · Disk usage critical — root fs ≥ 85% (priority high)
  • · Memory pressure — used ≥ 90% (priority high)
  • · Container(s) down — any of the 21 expected containers not running (priority max)
  • · Phones offline — adb sees < 3 handsets (priority high)
  • · Cloudflared tunnel unhealthy — telescope.vaspian.me not 200/302 (priority max)
  • · Recovery — any of the above clearing fires once (priority low)

How priorities are assigned

Mixed by event. Test submitted / phone-online / recoveries → low (2). Batch-complete scales with pass rate (low / default / high). Phone-offline, disk, memory, and worker errors → high (4). Containers-down and tunnel-unhealthy → max (5). Set per call site in integrations/ntfy.py and per alert in bin/sysmon.sh.

Example payloads

What an alert on this topic actually looks like on a phone.

Test complete - Acme Co - 88% pass
P2 · low white_check_marktelephone_receiver
44 clean / 4 labeled / 2 undelivered of 50 calls
Batch-complete push from the voice-worker. Tone keys off pass rate (≥80% → low/✅, 50–79% → default/⚠️, <50% → high/❌). On-device the title shows '-' separators, not '·' — the helper rewrites Unicode middots to ASCII before the request.
Phone offline - T-Mobile
P4 · high warningiphone
T-Mobile handset dropped off ADB.
DNIS: 7165551234
Check Wi-Fi + Wireless debugging on the phone.
Fired on the connected→disconnected edge by the phone watcher, not every poll. The matching 'Phone online' recovery fires at low priority when the handset comes back.
telescope - 2 container(s) down
P5 · max
  telescope-voice-worker-att
  telescope-asterisk
sysmon.sh host-health alert. Edge-triggered — fires once on healthy→unhealthy and stays silent until recovery, which fires its own 'telescope · containers (recovered)' at priority 2.

Notes

Subscribe on your phone
Topic name vaspian-telescope on server ntfy.sh.
Open in ntfy app