Observability export
Prometheus 0.0.4 scrape · OTLP/HTTP push

Send Streamwake telemetry
to your stack.

Two endpoints on the same admin-gated route — a Prometheus 0.0.4 scrape and an OTLP/HTTP push — so your observability team can wire Streamwake reliability signals into Prometheus, OpenTelemetry collectors, and Grafana without a custom agent on your side. Endpoint contracts and metric families below stay in lock-step with src/app/api/v1/metrics/route.ts.

Admin-scoped
Authenticate with the better-auth.session_token cookie of a platform admin.
Prometheus

Scrape in three lines.

Drop the snippet below into your Prometheus config, point the target at your Streamwake host, and the scraper picks up the families on the next collection interval.

The route replies with content-type: text/plain; version=0.0.4 so a stock Prometheus job ships it without a relabel_config. The authorization block carries the platform admin session; production enterprise will replace that with a dedicated observability bearer key.

metrics_path defaults to /metrics — override it to /api/v1/metrics/prometheus as shown.

prometheus.yml
scrape_configs:
  - job_name: streamwake
    metrics_path: /api/v1/metrics/prometheus
    scheme: https
    static_configs:
      - targets:
          - ${STREAMWAKE_HOST}
    authorization:
      type: Bearer
      credentials: ${STREAMWAKE_ADMIN_BEARER}
Manual scrape
curl https://streamwake.polsia.io/api/v1/metrics/prometheus \
  -H "authorization: Bearer ${STREAMWAKE_ADMIN_BEARER}" \
  -H "accept: text/plain; version=0.0.4"
200 Response — snippet of the exposition body
# HELP streamwake_stream_total Number of Streamwake streams segmented by their agent_state-derived status.
# TYPE streamwake_stream_total gauge
streamwake_stream_total{state="watching"} 12
streamwake_stream_total{state="down"} 1
streamwake_stream_total{state="degraded"} 0
# HELP streamwake_probe_total Streamwake probe attempts in the last 24 hours.
# TYPE streamwake_probe_total counter
streamwake_probe_total{stream_id="ckq3xstreamabc123",status="healthy"} 287
streamwake_probe_total{stream_id="ckq3xstreamabc123",status="degraded"} 1
streamwake_probe_total{stream_id="ckq3xstreamdef456",status="failing"} 14
# HELP streamwake_scrape_build_info Static build-info marker.
# TYPE streamwake_scrape_build_info info
streamwake_scrape_build_info{service="streamwake",format="prometheus"} 1
OpenTelemetry

Or push via OTLP/HTTP.

The same admin gate accepts an OTLP-shaped JSON envelope. The push endpoint is a stub sink today: it validates that resourceMetrics is a non-empty array and echoes the accepted count. v2 will persist the batch for replay; the contract is fixed so existing collectors keep working.

Wire the OTLP receiver in your OTel Collector with exporters.otlphttp pointing at this URL. The receiver validates the envelope shape — anything matching the OTLP ExportMetricsServiceRequest surface (lowercased to a single resourceMetrics array per the HTTP/JSON profile) is accepted.

Emitting 202 Accepted with acceptedCount matches OTLP's "received the batch" semantic — the receiver returns synchronously so a retrying exporter knows not to redeliver.

Push request
curl -X POST https://streamwake.polsia.io/api/v1/metrics/otlp \
  -H "content-type: application/json" \
  -H "authorization: Bearer ${STREAMWAKE_ADMIN_BEARER}" \
  -d '{
    "resourceMetrics": [
      { "resource": { "attributes": [] }, "scopeMetrics": [] }
    ]
  }'
Sample payload body
{
  "resourceMetrics": [
    {
      "resource": {
        "attributes": [
          {
            "key": "service.name",
            "value": {
              "stringValue": "streamwake-sidecar"
            }
          }
        ]
      },
      "scopeMetrics": []
    }
  ]
}
202 Response
{
  "accepted": true,
  "acceptedCount": 1,
  "rejectedCount": 0
}
Metric families

What lands in your scraper

Six families ship today, three gauges / two counters / one info marker. Names stay stable; label cardinality stays bounded by the underlying tables.

streamwake_stream_total
Number of Streamwake streams segmented by their agent_state-derived status (watching | down | degraded).
gauge
Labels
state
aggregated from Stream.agentState
streamwake_stream_uptime_pct
Last-24h uptime percentage per stream. Rows with no probes in the window render as 100%.
gauge
Labels
stream_id
source_domain
aggregated from StreamProbe over last 24h
streamwake_probe_total
Streamwake probe attempts in the last 24h, labelled by stream and probe status (healthy | degraded | failing).
counter
Labels
stream_id
status
aggregated from StreamProbe over last 24h
streamwake_probe_latency_ms
Probe latency quantiles (p50, p95, last) in milliseconds over the last 24h, per stream.
gauge
Labels
stream_id
quantile
aggregated from StreamProbe.latencyMs over last 24h
streamwake_incident_events_total
Incident timeline events recorded in the last 24h, labelled by severity and event type.
counter
Labels
severity
type
aggregated from IncidentEvent over last 24h
streamwake_scrape_build_info
Static build-info marker, one sample per (labelset, 1) per the Prometheus info-metric convention.
info
Labels
service
format
static (service=streamwake, format=prometheus)
On root-cause confidence

Confidence families ship when the persisted Incidents model lands; today we expose incident event counts but no numeric confidence. Today, IncidentEvent carries the timeline; ranked hypotheses live in a deterministic runtime seed. A numeric confidence series across scrapes is a v2 surface driven by the upcoming persisted Incident model.

Enablement

Hand it to the observability team.

Four steps to wire the cluster. Today the admin session doubles as the scraper credential — drop in a dedicated observability bearer key as part of an enterprise rollout and nothing here changes.

  1. 01
    Mint the scraper credential

    One platform admin signs in at /sign-in. The session cookie is the v1 credential; for production enterprise scrapes switch to a dedicated observability API key (see 'Known limits' below).

    No reverse proxy rewrites required.
  2. 02
    Point Prometheus at the route

    Add the snippet in the section above to prometheus.yml with metrics_path = /api/v1/metrics/prometheus.

    No reverse proxy rewrites required.
  3. 03
    Build dashboards from the families

    Per-stream uptime feeds a service-tier SLO. Probe-status counters feed an alert on a sustained failing streak. Incident event counts feed a secondary panel alongside PagerDuty / Slack acknowledgements.

    No reverse proxy rewrites required.
  4. 04
    Validate with the manual curl

    Hit curl https://streamwake.polsia.io/api/v1/metrics/prometheus from one operator laptop and confirm the body comes back as text/plain; version=0.0.4.

    No reverse proxy rewrites required.
Known limits
The deliberately small surface area at v1.
  • Auth is the platform admin cookie. v2 surfaces a dedicated observability bearer key so off-platform scrapers don't ride a human's session.
  • OTLP push is a stub sink — the route validates the envelope and echoes the accepted count. Persistence + downstream fan-out is a v2 surface.
  • Root-cause confidence is not yet a numeric series — it waits on the Incident model. Today we expose incident event counts.