feat(ops): observability — Workers Logs, health-check alert cron, weekly ops digest (0.0.8-rc.8) - #46
Conversation
…kly ops digest (0.0.8-rc.8) The cloud must not fail silently. Tonight's tail probe captured ZERO events — the account had no persistent Workers Logs, so structured failure lines (event=magic_link_send_failed) were emitted into the void, and nothing monitored health at all. Three legs: 1. Workers Logs on all four workers: [observability] enabled=true + head_sampling_rate=1 in both wrangler.tomls, top-level (production) AND [env.staging.observability] (observability is NOT inherited by named envs). 2. Health-check + alert cron (identity worker, src/ops.ts, [triggers] */10 * * * *): identity liveness = cheap D1 self-check; vault liveness = GET <VAULT_ORIGIN>/health (router-level, no DO wakeups). Failures email OPERATOR_ALERT_EMAIL with a 1-hour per-check dedupe persisted in D1 (ops_alerts; fail-open if D1 itself is down, mark-only-on-send-success). New public GET /health on the identity worker (the vault edge already had one). Staging runs the same crons; its devlog sender writes alert emails to the worker log — deterministic, no real email. 3. Weekly ops digest (same handler, cron "0 14 * * 1", Mon 14:00 UTC): counts only from D1 — users total/new-7d, vaults total/new-7d, magic links sent/failed-7d via the new magic_link_events per-day counters (bumped in the auth handler on every send outcome; PII-free by design: day+event+count, never an address/domain/IP; a broken counter can never break sign-in). Migration 0005_ops.sql (ops_alerts + magic_link_events). EmailSender grows a generic sendOps (binding + devlog). Smoke scripts check both /health endpoints. Gates: identity 111 passed (was 98; +12 ops.test.ts +1 counter test in auth.test.ts), vault 99 passed + 1 todo (+1 router /health), root 123 pass. Typechecks clean. Dry-run deploys clean for all four worker configs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XLZtmuSs1RirWGMGyCB1QB
…very env), soften observability-inheritance comment, exact dedupe-boundary test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XLZtmuSs1RirWGMGyCB1QB
|
Live cron proof (production): a passive outcome=ok with zero logs/exceptions = the health check ran and found both legs healthy (no Review folds in 3f0e2db (reviewer verdict was LGTM/no blockers): stale |
What / why
The cloud must not fail silently. Tonight's
wrangler tailprobe captured zero events — the account had no persistent Workers Logs enabled, so the structured failure lines we already emit (event=magic_link_send_failed, identity worker) went into the void, and nothing monitored health at all. This PR adds the three missing legs:1. Workers Logs on all four workers
[observability] enabled = true+head_sampling_rate = 1in both wrangler.tomls, at the top level (production) and[env.staging.observability]— observability is NOT inherited by named envs (per current wrangler docs), so it's stated explicitly in all four worker configs. Invocation logs +console.*are now persistently queryable (dashboard, logs API,wrangler tail).2. Health-check + alert cron (identity worker,
src/ops.ts)[triggers] crons = ["*/10 * * * *", "0 14 * * 1"]on the identity worker (no third worker);routeCron(controller.cron)dispatches, with unknown patterns degrading to the health check (never a silent no-op).Every 10 minutes:
SELECT 1) — a self-HTTP-fetch would be near-tautological (the worker obviously runs if the cron fired) and Workers can't fetch their own routes anyway;GET <VAULT_ORIGIN>/health(10s timeout) — the router-level endpoint that never wakes a DO (already existed; identity gained a new publicGET /healthfor monitors + the smoke scripts).Alerting: failures email
OPERATOR_ALERT_EMAIL(ag@unforced.org) via the existingsend_emailbinding. Dedupe: 1 hour per check, persisted in the new D1ops_alertstable; the mark is written only after a successful send (a failed email retries next tick), and the dedupe read fails open — if D1 itself is down we alert every 10 minutes rather than suppress the one email that matters. Every failure also logs a structuredevent=health_check_failedline independent of email. Staging runs the same crons, but has nosend_emailbinding by design → the devlog sender writes the full alert/digest email into the worker log — deterministic for tests, zero real email.3. Weekly ops digest (same handler, Mon 14:00 UTC ≈ Monday morning MT)
Counts-only from D1: users total/new-7d, vaults total/new-7d, magic links sent/failed-7d. The send counts come from the new
magic_link_eventsper-day counter table, bumped in the auth handler on every send outcome (the failure path that previously only logged now also counts). PII-free by design: rows are(day, event, count)— never an address, domain, or IP; the digest email is asserted in tests to contain no addresses or vault names. A broken counter can never break sign-in (bumpMagicLinkEventnever throws).Migration
0005_ops.sql(ops_alerts+magic_link_events).EmailSendergrows a genericsendOps(binding + devlog impls).Gates (literal)
workers/identity:tsc --noEmitclean; vitest 111 passed (was 98 — +12 newops.test.ts: cron routing/dispatch, dedupe window incl. failed-send retry, digest query shapes + PII-free assertion, /health shape, realworker.scheduledwiring viacreateScheduledController+ fetchMock; +1 counter test inauth.test.ts)workers/vault:tsc --noEmitclean; vitest 99 passed | 1 todo (was 98+1 — +1 router/healthshape test via SELF)bun test src123 pass, 0 fail (362 expect() calls)Live verification
Staging (
deploy-staging.sh): both crons registered (schedule: */10 * * * *,schedule: 0 14 * * 1in deploy output);GET /health→ 200{"status":"ok","service":"identity"}(identity) and{"status":"ok"}(vault); fullsmoke-staging.ts: 38 pass, 0 fail.Production (
deploy-prod.sh): custom domains retained, crons registered, migration applied;smoke-prod.ts: 13 pass, 0 fail (now includes the identity /health check).Tail proof (the blind-spot fix):
wrangler tail parachute-identitynow captures events —(version
2c5d49e9= this deploy). Tonight's identical probe captured nothing.Cron firing: a deployed cron can't be forced (
wrangler dev --test-scheduled+curl /__scheduled?cron=...is the local path; vitest drives the realworker.scheduledexport viacreateScheduledController). A passive production tail across a*/10boundary is attached in a PR comment if captured.🤖 Generated with Claude Code
https://claude.ai/code/session_01XLZtmuSs1RirWGMGyCB1QB