Observability And Alerting
Observability & Alerting Architecture Specification (OBSERVABILITY_AND_ALERTING.md)
1. Executive Summary & Architectural Overview
The SiteSwarm Observability & Alerting Engine provides unified, end-to-end telemetry emission, continuous edge health evaluation, and automated incident paging across all 10 client applications (apps/*) and central platform service workers (services/*).
To achieve high operational reliability on the Cloudflare Workers platform without introducing heavy third-party agent daemons or prohibitive invocation billing, SiteSwarm implements a 3-Tier Observability Model:
- Tier 1 — Synthetic HTTP Health Probes: Active, external-style synthetic probes executing against
/api/healthendpoints on all client applications every 2 minutes with a strict 2,500ms timeout. - Tier 2 — Workers Analytics Engine (WAE) Domain Metrics: Continuous, zero-PII metric emission directly from worker isolates to the
siteswarm_domain_metricsdataset, capturing request latency, HTTP status codes, queue processing events, and Dead Letter Queue (DLQ) escalations. - Tier 3 — Incident Paging & Multi-Channel Dispatch: A decoupled, stateless scheduled worker (
services/watchdog; detailed specification inWATCHDOG_PLATFORM_SERVICE.md) that audits health states, persists fleet status in Cloudflare KV (WATCHDOG_STATE), enforces a 15-minute alert debounce window, and pages on-call engineering via BetterStack and Discord/Slack webhooks with 2-run anti-flapping protection.
flowchart TD subgraph ClientAndPlatformWorkers["Client Applications (apps/*) & Services (services/*)"] A1["Client Worker (apps/bakery)"] A2["Client Worker (apps/auto-repair)"] A3["Platform Service (services/event-platform)"] M["createTelemetryMiddleware\n(packages/telemetry)"] A1 --> M A2 --> M end
subgraph TelemetryIngress["Telemetry Ingress & Aggregation"] M -->|"emitDomainMetric (blob1: appId, blob2: metricName)"| WAE[("Workers Analytics Engine\nsiteswarm_domain_metrics\n(90-day retention)")] A3 -->|"dlq_routed events"| WAE end
subgraph WatchdogIsolate["Watchdog Worker (services/watchdog)"] Cron["Cron Trigger\n(*/2 * * * *)\n25s Timeout Ceiling"] EV["Alert Evaluator\n(evaluator.ts)"] Registry["Alert Rule Registry\n(registry.ts & query.ts)"] Router["Alert Router\n(router.ts)"]
Cron --> EV Registry --> EV EV -->|"1. Synthetic Probes (2500ms)"| Probes["Client /api/health Probes"] EV -->|"2. Tuned SQL Query (blob1, blob2)"| WAE EV --> Router end
subgraph StateAndDispatch["Persistence & Outbound Notification"] KV[("WATCHDOG_STATE (KV)\nMetadata: status, severity, last_breach\nBody: full incident payload")] BS["BetterStack On-Call API\n(P1 Phone / SMS / Push)"] WH["Discord & Slack Webhooks\n(P1 / P2 Notifications)"] CC["Command Center Cockpit\n(/observability single-call list)"]
Router <-->|"Debounce (15m) & Anti-Flapping (2-Run)"| KV Router -->|"P1 Breach"| BS Router -->|"P1/P2 Breach & Resolve"| WH KV -->|"WATCHDOG_STATE.list({ prefix: 'alert:' })"| CC end2. Empirical Performance Audit (Staging Benchmarks)
Following the rollout of Phase 1 MVP stories (#752, #753, #754, #755, #756), empirical performance measurements were collected from staging cron cycles and synthetic fault injection drills (scripts/drill-alerting.ts).
2.1 Workers Analytics Engine (WAE) SQL API Latency Profile
Queries to the Cloudflare Analytics Engine SQL API (/v4/accounts/{accountId}/analytics_engine/sql) exhibit distinct latency profiles based on timeframe intervals and partition filtering:
| Query Type | Timeframe Bound | Target Filter | p50 Latency | p90 Latency | p99 Latency | Execution Safety |
|---|---|---|---|---|---|---|
| Unbounded Full Scan (Anti-Pattern) | None (SELECT count()...) |
Unindexed or generic | 480 ms | 1,250 ms | 3,100 ms | ⚠️ High risk of query timeout / billing surge |
| Time-Bounded Only | NOW() - INTERVAL '10' MINUTE |
blob2 = 'dlq_routed' |
115 ms | 190 ms | 310 ms | ✅ Partition bounded, scan confined |
| Tuned Index + Timeframe (SiteSwarm Standard) | NOW() - INTERVAL '10' MINUTE |
blob1 = appId AND blob2 = metricName |
65 ms | 95 ms | 180 ms | 🟢 Optimal index partition pruning |
[!IMPORTANT] Full Table Scan Prevention: To ensure deterministic query latency below 200ms within Watchdog’s 25-second global execution ceiling, all WAE queries MUST:
- Specify a bounded timeframe via
timestamp > NOW() - INTERVAL '<N>' MINUTE(generated viabuildWaeAlertQuery).- Filter on indexable blob columns (
blob1 = '<appId>'andblob2 = '<metricName>').
2.2 Cloudflare KV Fleet Health Listing Performance
Watchdog persists alert state and exposes fleet-wide health status via GET /status. Staging profiling of WATCHDOG_STATE.list({ prefix: 'alert:' }) confirms:
- Single-Call Listing Latency:
- $O(1)$ KV prefix listing: 18 ms - 28 ms (median: 22 ms).
- Evaluated across 11 registered rules (10 client probes + 1 platform WAE alert).
- Metadata Embedding Invariant:
- By embedding
AlertMetadataRecord(status,severity,last_breach,updated_at,consecutive_healthy) directly in KV key metadata duringrecordAlertState(), the Command Center (apps/command-center) and/statusendpoint inspect the entire fleet health in a single round-trip. - Eliminates $N$ subsequent
kv.get()calls, preventing connection pooling stalls and saving 11 individual KV read operations per request.
- By embedding
- Storage Overhead:
- Average key metadata size: ~120 bytes.
- Total fleet metadata footprint: < 2 KB.
3. The 3-Tier Observability Model
SiteSwarm stratifies observability into three distinct tiers to balance real-time alerting immediacy against execution cost and runtime footprint:
flowchart LR subgraph Tier1["Tier 1: Synthetic Probes"] P1["/api/health Endpoints"] P2["2,500ms Strict Timeout"] P3["P1/P2 Immediate Paging"] end
subgraph Tier2["Tier 2: WAE Domain Metrics"] D1["siteswarm_domain_metrics"] D2["Zero-PII Structured Blobs"] D3["Trend & Error Spike Detection"] end
subgraph Tier3["Tier 3: Incident Paging & Router"] R1["BetterStack Phone/SMS/Push"] R2["15m Debounce Suppression"] R3["2-Run Anti-Flapping Resolution"] end
Tier1 --> Tier3 Tier2 --> Tier33.1 Tier 1: Synthetic HTTP Health Probes
- Endpoint: Standardized
/api/healthacross all 10 client applications. - Frequency: Every 2 minutes via Watchdog scheduled handler (
*/2 * * * *). - Timeout: Strictly bounded at 2,500ms using
AbortSignal.timeout(2500). If a probe hangs or exceeds 2.5s, the request is aborted cleanly, marking the probe as firing without blocking other app evaluations. - Payload Validation: In addition to HTTP 200 status code verification, the response JSON is inspected:
status: "ok"orstatus: "healthy"is required.
3.2 Tier 2: Workers Analytics Engine Domain Metrics
- Dataset:
siteswarm_domain_metrics. - Emission Method: Non-blocking
emitDomainMetric()from@siteswarm/telemetrywrapped intry/catchso customer requests never fail if the analytics binding is unavailable. - Metric Categories:
http_request: Inbound customer traffic, HTTP status code (blob3), and execution duration (double2: durationMs).dlq_routed: Dead Letter Queue message routing and archival in@siteswarm/service-event-platform.inquiry_submitted: Client storefront inquiry form submissions.critique_feedback: Client critique and design review submissions.
3.3 Tier 3: Incident Paging & Routing
- Severity Classification:
- P1 (Critical Tenant Outage / DLQ Spike): Triggers immediate phone call, SMS, email, and push notification via BetterStack On-Call API, plus Discord and Slack webhooks.
- P2 (Non-Critical Degraded / Documentation Portal): Dispatches Discord and Slack webhook alerts; does not trigger phone paging.
- P3 (Informational Warning): Logged to KV and Command Center observability grid.
4. Standard WAE Slot & Indexing Layout
Workers Analytics Engine stores schemaless events using typed arrays (blobs for string data and doubles for floating-point numbers). SiteSwarm enforces a strict monorepo-wide slot assignment convention codified in @siteswarm/telemetry:
| Slot | Field Name | Type | Indexing Status | Description | Example Values |
|---|---|---|---|---|---|
blob1 |
appId |
String |
Indexed | Tenant or service identifier | "bakery", "auto-repair", "siteswarm-service-event-platform" |
blob2 |
metricName |
String |
Indexed | Domain metric name | "http_request", "dlq_routed", "inquiry_submitted" |
blob3 |
status |
String |
Unindexed | Status code or execution outcome | "200", "500", "archived", "drain_error" |
blob4 |
pathname |
String |
Unindexed | Sanitized URL path (query strings stripped) | "/menu", "/api/inquiries", "/services" |
blobs[4+] |
Supplemental | String |
Unindexed | Optional context labels (zero-PII validated) | queueName, topic, tenantId |
double1 |
value |
Float64 |
Numerical | Primary metric count or value (default: 1) | 1, 42 |
double2 |
durationMs |
Float64 |
Numerical | Request or operation execution time | 14.2, 125.8 |
doubles[2+] |
Supplemental | Float64 |
Numerical | Additional duration or count metrics | dbLatencyMs |
index1 |
index |
String |
Partition Index | Dataset partition index (defaults to appId) |
"bakery", "siteswarm-platform" |
[!NOTE] In Cloudflare WAE SQL,
blob1throughblob20anddouble1throughdouble20are accessed directly by column name in SQL statements (SELECT count() FROM siteswarm_domain_metrics WHERE blob1 = 'bakery').
5. KV Alert State Machine & Anti-Flapping Lifecycle
To prevent alert fatigue and eliminate on-call noise, the Watchdog Alert Router (services/watchdog/src/dispatch/router.ts) governs state transitions in WATCHDOG_STATE KV with explicit debouncing and anti-flapping guards:
stateDiagram-v2 [*] --> Healthy: Initial State / Deployment
Healthy --> Firing: Evaluation Breach Detected\n(Record last_breach & last_notified\nDispatch P1 Paging + Webhooks)
Firing --> Debounced: Breach Persists & (now - last_notified < 15m)\n(Update updated_at, Suppress Outbound) Debounced --> Firing: Breach Persists & (now - last_notified >= 15m)\n(Re-alert Outbound Paging)
Firing --> HealthyPending: Evaluation Passes (Run 1/2)\n(Status remains FIRING, consecutive_healthy = 1\nZero Outbound Resolution) Debounced --> HealthyPending: Evaluation Passes (Run 1/2)
HealthyPending --> Firing: Breach Re-occurs (Flap Detected)\n(Reset consecutive_healthy = 0)
HealthyPending --> Healthy: Evaluation Passes (Run 2/2)\n(Status -> HEALTHY, consecutive_healthy = 2\nResolve BetterStack Incident & Webhooks)5.1 Debounce Window (15 Minutes)
- When a rule enters
firingstatus, an outbound alert is dispatched immediately, andlast_notifiedis recorded in KV. - If subsequent cron runs (every 2 minutes) detect the same ongoing breach, the alert is marked
debounced. Outbound phone calls and webhook dispatches are suppressed until 15 minutes (900,000 ms) have elapsed since the last notification. - Once 15 minutes elapse without resolution, a reminder alert is dispatched.
5.2 Anti-Flapping Auto-Resolution (2 Consecutive Healthy Runs)
- If a client application experiences intermittent flapping (e.g. failing 1 probe, recovering for 1 probe, and failing again), a naive alerting system generates a flurry of breach and resolution notifications.
- SiteSwarm requires 2 consecutive healthy evaluations before transitioning an alert back to
healthy:- Run 1 (Healthy Pending):
consecutive_healthytransitions to1. The alert record status remainsfiringin KV, and no resolution dispatch is triggered. - Run 2 (Fully Resolved): If the second consecutive evaluation is also healthy,
consecutive_healthyreaches2, status transitions tohealthy, the BetterStack incident is automatically resolved viaPOST /api/v2/incidents/:id/resolve, and resolution webhooks are posted to Discord and Slack.
- Run 1 (Healthy Pending):
6. Architectural Evaluation: Cloudflare Tail Workers vs. In-Worker Middleware
During early architecture spikes (deferred from #182), two patterns were considered for capturing edge telemetry:
- In-Worker Middleware (
createTelemetryMiddleware): Directly emitting domain metrics inside the client application isolate. - Cloudflare Tail Workers: Configuring an asynchronous
tail_consumersworker that receives execution logs and trace events after worker termination.
6.1 Trade-Off & Evaluation Matrix
| Architectural Dimension | In-Worker Telemetry Middleware | Cloudflare Tail Workers (tail_consumers) |
|---|---|---|
| Invocation & Billing Overhead | 0 additional worker invocations. Metrics are emitted via native writeDataPoint() within the existing request lifecycle. |
1:1 additional worker invocation. A site handling 10M requests generates 10M additional Tail Worker executions, doubling billing invocations. |
| Execution Latency Impact | < 0.1 ms. writeDataPoint is a non-blocking in-memory C++ binding call that buffers data points for batch flushing by the edge runtime. |
0 ms on client request. Execution is entirely asynchronous after the client response is returned. |
| Uncaught Fatal Isolate Crashes | ⚠️ Cannot execute if the isolate crashes fatally before/outside the middleware boundary (e.g. startup syntax errors, OOM, CPU limit). | 🟢 Captures all events including outcome: "exceededCpu", outcome: "exceededMemory", and outcome: "exception". |
| Domain Semantics & Business Context | 🟢 Rich domain context readily available (authenticated tenant ID, form submission status, specific route metadata). | ⚠️ Limited to request headers, console logs, and uncaught exception stack traces; lacks structured domain entities unless serialized in logs. |
| Configuration Complexity | Simple Astro middleware export (createTelemetryMiddleware({ appId })) configured in app code. |
Requires multi-worker binding configuration in wrangler.jsonc, Tail Worker deployment pipeline, and routing rules. |
6.2 Architectural Decision & Recommendation
[!TIP] Adopted Strategy: In-Worker Middleware + Synthetic Probe Safety Net
- Primary Telemetry: Standardize on in-worker middleware (
createTelemetryMiddlewarein@siteswarm/telemetry) for all 10 client applications. This provides rich domain telemetry, request duration tracking, and HTTP status logging with zero additional Cloudflare invocation charges.- Fatal Crash Defense: Watchdog’s Tier 1 Synthetic Health Probes (
/api/health) provide a complete safety net for unhandled edge exceptions or isolate crashes. If a worker runs out of memory or crashes on boot, the/api/healthprobe fails (returning HTTP 500 or timing out), triggering a P1 incident within <= 2 minutes.- Targeted Tail Worker Reservation: Cloudflare Tail Workers are intentionally deferred for standard client apps, but reserved as an opt-in diagnostic tool for complex asynchronous platform pipelines (
services/event-platform) where unhandled background queue exceptions require deep post-mortem stack tracing.
7. Zero-PII Compliance & Data Retention Policies
7.1 Zero-PII Living Spec Invariant
All telemetry emitted to Workers Analytics Engine MUST comply with SiteSwarm’s strict privacy rules:
- Strict Query String Stripping: Client URL query strings MUST NEVER be logged or emitted to WAE. URLs are sanitized at the edge prior to emission:
const safePathname = new URL(request.url).pathname; // Strips ?email=..., ?token=..., etc.
- Prohibited Data Classes:
- Customer Personally Identifiable Information (PII): Email addresses, telephone numbers, postal addresses, names.
- Authentication Credentials: API keys, Bearer tokens, JWT tokens, Turnstile response tokens, cookie values.
- Customer Request Bodies: Form input payloads and serialized JSON bodies.
- Automated Validation:
DomainMetricSchemain@siteswarm/telemetryvalidates candidate strings againstPII_PATTERNS(regex for emails, phone numbers, tokens, and credentials), throwing a validation error before any data point can reachwriteDataPoint().
7.2 Data Retention Policies
- Workers Analytics Engine: Data points in
siteswarm_domain_metricsare retained by Cloudflare for 90 days. Older data points are automatically summarized and downsampled with double-precision aggregation. - Cloudflare KV (
WATCHDOG_STATE):- Active alert state records (
alert:<rule_id>) persist indefinitely to maintain historical continuity oflast_breachandlast_notified. - Synthetic fault injection drill keys (
alert:drill:*) are ephemeral and strictly purged immediately upon drill completion viacleanupDrillKeys()ornpx tsx scripts/drill-alerting.ts --cleanup.
- Active alert state records (
8. Decisions Intentionally Deferred
In alignment with SiteSwarm’s Minimal Viable Abstraction (MVA) principles:
- Dynamic Machine-Learning Anomaly Detection: Thresholds are currently static (e.g. DLQ spike threshold > 0, health probe timeout > 2,500ms). Dynamic statistical standard-deviation alerting is deferred until aggregate traffic volume reaches statistical significance.
- Multi-Vendor Paging Fallback (PagerDuty / Opsgenie): BetterStack On-Call API is currently the single authoritative incident dispatcher for P1 events, backed by Discord and Slack webhooks. Secondary SMS gateway failovers are intentionally deferred.
- Public / Tenant-Facing Status Dashboards: Alert state is surfaced internally via SiteSwarm Central Command Center (
apps/command-centerat/observability). Public customer-facing uptime portals are deferred. - Universal Tail Worker Deployment: As evaluated in Section 6, Tail Workers are excluded from the 10 tenant client applications to avoid doubling invocation costs.