Observability bundle
This page documents the shipped operator observability bundle for Spooky.
It is the packaging guide for the artifacts under:
deploy/observability/grafana/deploy/observability/prometheus/recording-rules.yamldeploy/observability/prometheus/alerts.yamldeploy/observability/slo/
Use this page when you need to answer:
- which dashboard to open first
- what each alert severity means
- how to interpret the packaged SLOs
- how to correlate one incident across metrics, logs, traces, control API, and audit
- how to roll out or version the observability package safely
The canonical observability vocabulary still lives in
docs/architecture/observability-contract.md
and crates/edge/src/observability/mod.rs. This page describes the shipped
operator experience built on top of that contract.
Open This First
Use this table when you need a fast starting point during an incident:
| Symptom | First dashboard | First runtime view | Why |
|---|---|---|---|
| customer-visible 5xx or latency regression | edge-traffic.json |
GET /admin/runtime |
start broad, then localize by upstream or backend |
| rising 429s or policy denials | admission-overload.json |
GET /admin/runtime |
separates quota, overload, auth, and rate-limit outcomes |
| rising backend timeouts or backend 5xx concentration | backend-health.json |
GET /admin/runtime |
ties request failures to backend lifecycle and DNS state |
| retry or hedge growth | retries-hedges.json |
GET /admin/runtime |
shows whether resilience is helping or amplifying failure |
| handshake or certificate trouble | tls-certificates.json |
GET /admin/runtime |
separates downstream TLS, upstream TLS, and cert expiry |
| activation, rollback, or restart trouble | control-plane.json |
GET /admin/runtime/history |
shows runtime generation state and recent control-plane activity |
Shipped Package
The current package is made of four layers:
- recording rules
- alert rules
- SLO definitions
- Grafana dashboards
The operator-facing contract version and audit schema version are surfaced in the control API runtime views. At this point the packaged values are:
- observability contract version:
v1 - audit schema version:
v1
Package Inventory
Prometheus recording rules
File:
deploy/observability/prometheus/recording-rules.yaml
Purpose:
- turn raw scrape-time series into stable operator queries
- keep dashboards and alerts off ad hoc raw PromQL
- provide bounded-cardinality rollups for traffic, admission, backend, TLS, quota, and control-plane views
Prometheus alert rules
File:
deploy/observability/prometheus/alerts.yaml
Purpose:
- define page-level and ticket-level production alerts
- consume recording rules instead of duplicating raw query logic
- map every alert to a concrete runbook or operations document
SLO package
Files:
deploy/observability/slo/README.mddeploy/observability/slo/definitions.promql
Purpose:
- lock the numerator and denominator contracts for operator reporting
- keep overload, quota, auth, and backend timeout concepts separate
- provide the canonical 30-minute reporting layer used by dashboards and burn-rate alerts
Grafana dashboards
Files:
deploy/observability/grafana/edge-traffic.jsondeploy/observability/grafana/admission-overload.jsondeploy/observability/grafana/backend-health.jsondeploy/observability/grafana/retries-hedges.jsondeploy/observability/grafana/tls-certificates.jsondeploy/observability/grafana/control-plane.json
Purpose:
- provide a packaged operator workflow instead of leaving teams to assemble dashboards ad hoc
- make the distinction between traffic failure, overload, quota, auth, backend health, TLS, and control-plane state obvious
Dashboard Guide
edge-traffic.json
Dashboard:
- title:
Spooky Edge Traffic and Latency - uid:
spooky-edge-traffic
Open this first for:
- request volume shifts
- success-rate regression
- p50, p95, or p99 latency growth
- failing upstream or backend concentration
- active runtime generation drift across the fleet
Primary panels:
Request RateSuccess RateP50 LatencyP95 LatencyP99 LatencyTraffic Volume by UpstreamStatus Class MixUpstream Outcome MixTop Failing UpstreamsTop Failing Backends
Operator intent:
- this is the entry dashboard for customer-visible traffic symptoms
- use it to decide whether the problem is broad, upstream-localized, or backend-localized before opening specialized dashboards
- from here, the most common next move is either
backend-health.jsonfor timeout-heavy failures oradmission-overload.jsonfor 503s that might be self-protection
admission-overload.json
Dashboard:
- title:
Spooky Admission, Overload, Quota, and Auth - uid:
spooky-admission-overload
Open this when:
- 429 or policy denials rise
- 503s might be overload-related
- brownout or adaptive admission is suspected
- quota backend degradation is reported
- auth denials or auth dependency failures increase
Primary panels:
Brownout StateOverload Shed RateCircuit Open Reject RateScoped Rate Limit Reject RateOverload Shed by ReasonAdaptive Admission BehaviorQuota DecisionsQuota Backend HealthTop Quota Denials by Policy and ReasonAuth Denied vs UnavailableAuth Contract Detail
Operator intent:
- preserve the runtime boundary between overload control and policy-contract failure
- do not interpret quota denial as overload
- do not interpret auth unavailability as quota exhaustion
- if quota-backend degradation is visible here, confirm runtime state in
GET /admin/runtimebefore changing policy
backend-health.json
Dashboard:
- title:
Spooky Backend Health and DNS Lifecycle - uid:
spooky-backend-health
Open this when:
- backend timeouts are rising
- backend errors are concentrated on a subset of upstream/backend pairs
- active probe failures and request-path failures disagree
- DNS refresh or topology churn may be driving instability
Primary panels:
Health Check Failure RatioBackend Timeout RateBackend Error RateDNS Refresh Failure RatioActive Health Check OutcomesPassive Health Failures by ReasonBackend Timeout and Error PressureTop Backend Errors by Upstream and BackendDNS Refresh Outcomes and Address-Set ChangesClient Rotations and FailuresResolved Addresses by BackendLast Successful DNS Refresh Age by Backend
Operator intent:
- separate active health probes from passive request-path failures
- show when DNS churn or stale resolver state is the real cause of backend instability
- if this dashboard shows churn or degraded health while traffic is failing, the next control-plane read should usually be
GET /admin/runtime
retries-hedges.json
Dashboard:
- title:
Spooky Retries and Hedges - uid:
spooky-retries-hedges
Open this when:
- retries increase faster than raw request failure
- hedge activity appears to be masking latency regression
- duplicate work might be amplifying backend pressure
Primary panels:
Retry Attempt RateRetry RatioRetry ReasonsRetry Denials by ReasonRetry Pressure vs Backend Failure PressureHedge Trigger RateHedge Waste RatioHedge Trigger, Win, Waste, and Primary-Won PatternsHedge Effectiveness RatiosAverage Primary Late Time After Hedge TriggerPrimary Late Sample Rate
Operator intent:
- distinguish resilience behavior that is helping from resilience behavior that is amplifying failure
- read retry growth and hedge waste together with backend timeout pressure
- if hedge growth is high but user-visible latency is still rising, treat it as backend stress rather than success
tls-certificates.json
Dashboard:
- title:
Spooky TLS Certificates and Handshakes - uid:
spooky-tls-certificates
Open this when:
- handshake failures rise
- a certificate rotation is in progress
- ALPN negotiation or SNI selection looks wrong
- upstream TLS failures might be request-path specific
Primary panels:
Handshake Failure RatioHandshake Failure RateMinimum Certificate Days RemainingUpstream TLS Failure RateDownstream Handshake Failures by Listener and ReasonTop Upstream TLS Failures by Backend, Phase, and ReasonLowest Certificate Days RemainingCertificate Selection Outcomes by ListenerNegotiated ALPN by Listener
Operator intent:
- separate downstream handshake problems from upstream TLS request-path failures
- make certificate expiry and unexpected selection behavior visible before client impact broadens
- after a certificate change, confirm both the dashboard and the runtime snapshot before expanding rollout
control-plane.json
Dashboard:
- title:
Spooky Control-Plane Activity - uid:
spooky-control-plane
Open this when:
- reload, activation, rollback, or restart workflows fail
- watchdog or runtime panic signals appear
- audit-correlated control-plane activity needs validation
- runtime generation state might be drifting across the fleet
Primary panels:
Runtime StateActive Runtime GenerationRuntime History DepthAudit-Correlated Control-Plane Activity RateControl-Plane Error Event RateValidation, Preview, Activation, and Rollback ActivityRuntime Outcomes by Result and ReasonRuntime Rejections by ReasonWatchdog State and Runtime HealthControl API Pressure and Error Events
Operator intent:
- this is the admin-plane dashboard, not a traffic dashboard
- use it to correlate runtime operations with emitted audit events and to verify whether the control plane is healthy enough to support incident response
Runtime Introspection Entry Points
The control plane is part of the observability bundle, not a separate concern.
Use these endpoints as the runtime-introspection entry points:
GET /admin/runtimeUse this for current runtime state, observability package metadata, backend health summary, quota backend health summary, watchdog state, and recent admin actions.GET /admin/runtime/historyUse this for retained generations, activation history, rollback candidates, and generation-scoped operator history.GET /admin/runtime/history/{generation}Use this when one generation needs focused review during rollback or activation debugging.
When dashboards show trend but not current state, these are the next calls to make.
Audit As The Operator History Surface
The audit stream is the control-plane event history for:
- authentication and authorization outcomes
- validate, preview, activate, rollback, reload, restart, and cert reload attempts
- attempt versus result correlation
- actor attribution and peer attribution
The current audit schema version is v1, which matches the code-defined ADMIN_AUDIT_SCHEMA_VERSION.
Operators should treat audit as:
- the source of truth for who initiated a control-plane action
- the source of truth for whether the action succeeded, was denied, or failed
- the place to verify
requested_by, actor roles, authn mechanisms, generation movement, and canonical reason or failure class
Metrics show rate and trend. Audit shows action sequence and attribution.
Alert Severity And Runbook Mapping
The alert package has two severities:
page: operator action is expected immediately because user impact or control-plane safety is already at riskticket: the issue is meaningful and should be investigated, but it is not yet assumed to require immediate paging
Page alerts
| Alert | Meaning | First dashboard | Runbook |
|---|---|---|---|
SpookyAvailabilityBurnRatePage |
5xx availability budget is burning in fast and sustained windows | edge-traffic.json |
docs/operations/runbook.md#scenario-rising-503-rate |
SpookyP99LatencyBurnPage |
p99 latency is materially above the packaged objective in fast and sustained windows | edge-traffic.json |
docs/operations/runbook.md#scenario-backend-timeout-surge |
SpookyBackendTimeoutSurgePage |
backend timeout ratio is high enough to threaten user-visible reliability | backend-health.json |
docs/operations/runbook.md#scenario-backend-timeout-surge |
SpookyTlsCertificateExpiryCritical |
at least one downstream certificate has fewer than 7 days remaining | tls-certificates.json |
docs/operations/runbook.md#scenario-cert-rotation |
SpookyWatchdogDegraded |
watchdog degraded windows are being recorded continuously | control-plane.json |
docs/operations/runbook.md#scenario-control-api-or-metrics-endpoint-unavailable |
SpookyRuntimePanicObserved |
runtime panic counters are non-zero in the recent window | control-plane.json |
docs/operations/runbook.md#after-any-incident |
SpookyControlPlaneUnavailable |
packaged control-plane recording-rule series are absent | control-plane.json |
docs/operations/runbook.md#scenario-control-api-or-metrics-endpoint-unavailable |
Ticket alerts
| Alert | Meaning | First dashboard | Runbook |
|---|---|---|---|
SpookyRetryGrowth |
retry ratio is rising and may be amplifying backend trouble | retries-hedges.json |
docs/operations/runbook.md#scenario-backend-timeout-surge |
SpookyHedgeGrowth |
hedge activity is becoming routine rather than exceptional | retries-hedges.json |
docs/operations/runbook.md#scenario-backend-timeout-surge |
SpookyBackendDnsRefreshFailures |
DNS refresh failures and ratio indicate stale or unstable backend resolution | backend-health.json |
docs/operations/runbook.md#scenario-backend-timeout-surge |
SpookyBrownoutActiveTooLong |
brownout is staying active beyond the tolerated window | admission-overload.json |
docs/operations/runbook.md#scenario-brownout-or-overload-triggering |
SpookyQuotaBackendDegraded |
the distributed quota backend is timing out, unavailable, or otherwise degraded | admission-overload.json |
docs/operations/distributed-quota.md |
SpookyTlsHandshakeFailuresRising |
downstream handshake failures are rising above normal background levels | tls-certificates.json |
docs/operations/runbook.md#scenario-handshake-failures-or-client-connection-failures |
SLO Interpretation
The SLO package is defined in deploy/observability/slo/. Use the packaged
spooky:slo_* series for reporting rather than rebuilding numerator and
denominator logic in Grafana.
Availability
Primary series:
spooky:slo_availability_ratio:30mspooky:slo_request_server_error_ratio:rate30m
Interpretation:
- this is the coarse user-visible server-failure contract
- upstream
5xx, overload-generated503, and backend failures that surface as5xxcount here - auth denials, quota denials, and scoped rate limits do not count here
Latency
Primary series:
spooky:slo_request_latency_ms:p50_30mspooky:slo_request_latency_ms:p95_30mspooky:slo_request_latency_ms:p99_30m
Interpretation:
- these percentiles describe successful service only
- rejected work, timeout outcomes, and quota or auth denials are intentionally excluded
- use the edge traffic dashboard for cluster-wide latency, then move to backend or retry dashboards if tails deteriorate
Overload shed rate
Primary series:
spooky:slo_overload_shed_ratio:30m
Interpretation:
- only canonical overload-control decisions count
- quota denials, auth denials, and scoped rate limits are excluded
- this is a capacity-protection signal, not a general policy-denial signal
Backend timeout rate
Primary series:
spooky:slo_backend_timeout_ratio:30m
Interpretation:
- this is narrower than overall backend failure
- it should be read with
backend-health.jsonandretries-hedges.json - do not silently widen it into a generic backend error SLO
Auth failure rate
Primary series:
spooky:slo_auth_denial_ratio:30m
Interpretation:
- this covers explicit auth contract failures such as external auth denial and local JWT validation failure
- it does not fold in generic policy rejection or fail-open dependency errors
Quota denial operator view
Primary series:
spooky:slo_quota_denial_ratio:30m
Interpretation:
- quota is a contract-enforcement view, not overload
- quota backend degradation should be interpreted alongside quota backend health, not mislabeled as ordinary traffic failure
Correlation Workflow
The observability bundle is designed so an operator can start from any one surface and move to the others without translating vocabulary.
The stable correlation fields are:
request_idtrace_idspan_idevent_idgenerationlistenerrouteupstreambackendreasonfailure_classpolicycomponent
The source of truth for reason and decision vocabulary is:
crates/edge/src/observability/mod.rs
Alert to dashboard
- Read the firing series labels.
- Keep the canonical low-cardinality labels intact:
upstream,backend,listener,reason,decision,policy,backend_mode. - Open the dashboard named in the alert mapping above.
- Validate whether the issue is traffic, admission, backend, TLS, or control-plane scoped.
Dashboard to control API
Use the control API when you need current runtime state rather than trend.
Look for:
- current runtime generation
- backend lifecycle and membership state
- quota backend availability and recent errors
- active observability contract version
- recent admin actions
- audit schema version
The main runtime entry points are documented in Control Plane.
When the runtime snapshot is open, the high-signal fields to inspect first are:
observability.contract_versionobservability.audit_schema_versionobservability.current_generationobservability.backend_health_summaryobservability.quota_backend_health_summaryobservability.recent_admin_actions
Dashboard to logs and traces
Use logs and traces when the packaged metrics explain the class of failure but not the specific request path.
Look for the same canonical names:
- logs:
request_id,upstream,backend,reason,failure_class - traces:
trace_id,span_id,generation,listener,upstream,backend,reason
Do not correlate by prose strings when a canonical slug is available.
Control API to audit
Use the audit stream when the incident involves:
- reload or activation attempts
- rollback decisions
- control-plane auth failures
- certificate reload attempts
- actor attribution
Audit is the source of truth for control-plane action history. Metrics and dashboards show rate and trend; audit shows who did what, against which target, with which generation and result.
Metrics to audit
Move directly from metrics to audit when:
- control-plane errors or restart requests rise
- runtime activation or rollback outcomes change unexpectedly
- auth failures are clearly on the admin plane rather than the request path
Typical sequence:
- identify the metric family and time window
- open
control-plane.json - read
GET /admin/runtimeorGET /admin/runtime/history - use audit events for actor, action, and result attribution
Incident Workflows
Rising 5xx or latency page
- Open
edge-traffic.json. - Check whether
Top Failing UpstreamsorTop Failing Backendsis concentrated. - If timeout-heavy, move to
backend-health.json. - If retry or hedge growth is visible, move to
retries-hedges.json. - If 503s are overload-generated rather than upstream-generated, move to
admission-overload.json.
Quota backend degraded
- Open
admission-overload.json. - Read
Quota Decisions,Quota Backend Health, andTop Quota Denials by Policy and Reason. - Confirm current backend mode and degraded state in the control API runtime snapshot.
- Use Distributed Quota for fail-open, fail-closed, and fallback interpretation.
TLS or certificate incident
- Open
tls-certificates.json. - Separate downstream handshake failures from upstream TLS failures.
- Check the certificate days-remaining panels before assuming the issue is trust or routing.
- If the issue followed a certificate or listener change, use the runbook certificate-rotation path.
Control-plane incident
- Open
control-plane.json. - Check runtime generation, recent activity rate, runtime outcomes, watchdog state, and control API pressure.
- Read runtime snapshot and runtime history from the control API.
- Use audit records for actor attribution and operation sequence.
Rollout And Versioning
Prometheus rollout order
- Load
deploy/observability/prometheus/recording-rules.yaml. - Confirm the
spooky:*recording-rule series are present. - Load
deploy/observability/prometheus/alerts.yaml. - Validate that every alert evaluates against recording rules rather than missing raw series.
- Only then import or refresh dashboards.
Do not roll out alert rules before the recording rules they consume.
Grafana import rules
- import the dashboard JSON files from
deploy/observability/grafana/ - preserve the packaged
uidvalues so links, references, and operator runbooks stay stable - treat the repo JSON as the source of truth rather than hand-editing panels in production
- if you customize a dashboard locally, either keep it clearly separate or upstream the change into the repo package
Current packaged dashboard JSON files all ship with explicit version fields.
Treat a dashboard version bump in the repo as an intentional artifact change
that should be reviewed together with alert, SLO, or recording-rule changes.
Control API compatibility checks
After rollout, confirm the control API runtime snapshot exposes:
observability.contract_versionobservability.audit_schema_versionobservability.dashboard_packagesobservability.documentation
This is the runtime proof that the node is serving the expected operator bundle contract.
Safe production update pattern
- update recording rules
- wait for the new recording series to appear
- update alerts
- import dashboard JSON changes
- verify runtime snapshot metadata
- verify one page alert, one ticket alert, and one dashboard query in staging or canary before fleet-wide rollout