Telemetry
OpenCloud provides a stable aggregate for reading health and a bounded custom metric API for app-specific product signals. Prometheus, Loki, and Grafana are platform implementation details.
telemetry.summary()
Returns a safe, host-bound aggregate. It contains no raw logs, request paths, internal labels, or datasource credentials.
const summary = await opencloud.telemetry.summary();
const rest = summary.activity.surfaces.rest;
renderCount(rest.requests24h);
renderFreshness(summary.activity.telemetry.latestIngestedAt);Exact response shape
{
"appId": "6f9619ff-8b86-4e6e-a62a-889950f42d3e",
"asOf": "2026-07-28T12:00:00.000Z",
"usage": {
"windowStart": "2026-07-27T00:00:00.000Z",
"windowEnd": "2026-07-28T00:00:00.000Z",
"calculationVersion": "v1",
"completeness": "partial",
"metrics": {
"databaseStorageBytes": 4096,
"fileStorageBytes": 1024,
"users": 2
},
"createdAt": "2026-07-28T00:05:00.000Z"
},
"activity": {
"window": {
"from": "2026-07-27T12:00:00.000Z",
"to": "2026-07-28T12:00:00.000Z",
"seconds": 86400
},
"telemetry": {
"status": "available",
"latestIngestedAt": "2026-07-28T11:59:58.000Z",
"ingestionLagSeconds": 0.32,
"sampledEntries": 241,
"truncated": false
},
"surfaces": {
"page": {
"lastActivityAt": "2026-07-28T11:59:55.000Z",
"requests24h": 80,
"errors24h": 0,
"lastStatus": 200
},
"rest": {
"lastActivityAt": "2026-07-28T11:59:50.000Z",
"requests24h": 120,
"errors24h": 1,
"lastStatus": 200
},
"storage": {
"lastActivityAt": null,
"requests24h": 0,
"errors24h": 0,
"lastStatus": null
},
"realtime": {
"lastActivityAt": null,
"requests24h": 0,
"errors24h": 0,
"lastStatus": null
},
"function": {
"lastActivityAt": null,
"requests24h": 0,
"errors24h": 0,
"lastStatus": null
},
"cron": {
"lastActivityAt": "2026-07-28T11:55:00.000Z",
"requests24h": 24,
"errors24h": 0,
"lastStatus": 200
}
}
}
}All six surface keys are always present. usage can be null.
Freshness rules
telemetry.status: "unavailable"means activity could not be read.latestIngestedAt: nullmeans no sampled ingestion evidence exists.truncated: truemeans the 5,000-entry sample limit was reached.- A zero request count is meaningful only when telemetry is available and the sample is not truncated.
- Missing activity is unknown or quiet, never “healthy.”
Custom counters and gauges
Declare each custom metric in opencloud.yaml before emitting it:
observability:
metrics:
- name: tasks_created
type: counter
unit: tasks
dimensions:
assignee_type:
values: [parent, child]
- name: overdue_tasks
type: gauge
unit: tasksEmit values through the deployment-pinned SDK:
await opencloud.telemetry.increment("tasks_created", 1, {
dimensions: { assignee_type: "child" },
idempotencyKey: `task-created:${task.id}`,
});
await opencloud.telemetry.gauge("overdue_tasks", 7);An idempotency key prevents an exact retried write in the same deployment from being counted twice. Reusing the key with a different measurement is rejected. Browser values can be manipulated by the visitor, so treat them as product signals rather than security or billing evidence.
An app can ingest at most 1,200 measurements per minute and retain at most 100,000 points. Points older than 14 days are removed during ingestion. A request that exceeds an ingestion or retained-point limit returns HTTP 429.
The first release supports counters, gauges, fixed windows, bounded enum dimensions, and simple threshold rules. Histograms, calculated metrics, arbitrary queries, notification channels, and automatic remediation are not part of this contract.
Agent Feed
Agents read one stable, app-scoped summary instead of depending on Prometheus, Loki, or Grafana response formats:
GET /v1/apps/{appId}/agent-feed?since=2026-07-29T10:00:00.000Z
Authorization: Bearer <app-scoped credential>The response has contractVersion: "1" and contains:
- Current app and active-deployment state.
- Telemetry freshness, bounded built-in signals, and each declared custom metric (
sumfor counters andlatestfor gauges over 15 minutes). - Firing custom alerts and recently resolved transitions.
- Up to 100 recent deployment-operation and cron events.
eventsTruncated, which is true when more recent events matched than fit.nextSince, which can be passed to the next poll.
Built-in alerts cover failed app/deployment state, recent operation or cron failure, high HTTP error rate, and unavailable or stale runtime telemetry. Missing activity is not reported as healthy. Metrics originating in a browser remain explicitly marked with source: "browser"; authenticated-browser, mixed, and no-sample provenance are also distinguished.
Alert rules
Alert rules are app-scoped, limited to 20 per app, and can reference only a metric declared by the active deployment:
PUT /v1/apps/{appId}/alert-rules/too-many-overdue
Content-Type: application/json
{
"name": "Too many overdue tasks",
"metric": "overdue_tasks",
"aggregation": "latest",
"operator": "gt",
"threshold": 10,
"window": "15m",
"minimumSamples": 1,
"severity": "warning",
"enabled": true
}Use GET /v1/apps/{appId}/alert-rules to list rules and DELETE /v1/apps/{appId}/alert-rules/{ruleId} to remove one. Counters support sum and per-second rate; gauges support latest, min, max, and avg. Operators are gt, gte, lt, lte, and eq. Windows are 5m, 15m, 1h, and 24h.
Rules are evaluated when the Agent Feed is read and their state is persisted as ok, firing, resolved, unknown, or invalid. This first release has no scheduler, Alertmanager dependency, notification routing, or automatic remediation.
Custom measurements and alert state are stored app-scoped in the control database with a maximum 14-day measurement retention. Prometheus/Loki/Grafana remain internal diagnostics rather than application contracts.