Pulse
Explore metrics, events, states, resources, queries, and dashboards in one telemetry workspace.
Pulse turns incoming metrics, events, and current states into browsable resources, reusable queries, and dashboards. It can describe infrastructure, application behavior, or business activity without requiring one fixed domain model.
Set up the data store
A Cloud admin connects an operator-provided ClickHouse instance in Administration → Pulse → Data store. The page shows three steps: provide a dedicated ClickHouse 26.9 or newer instance, run the setup script as its admin, then enter the service address and credentials and choose Check and connect. Pulse checks the connection, version, rights and read-profile limits before saving. A failed check names the problem and how to fix it; the saved connection stays unchanged.
Operators can use cld pulse admin store get|set|check|setup-sql for the same
workflow. Password input uses a private file or stdin. See
Provide ClickHouse for Pulse
for memory, isolation and backup requirements.
This release prepares the connection only. Existing Pulse features continue using Postgres and work without ClickHouse; later releases require the store.
Use Pulse
- Check Sources first when expected data is missing or stale.
- Browse Resources when the question concerns one host, container, service, customer, order, or other observed object.
- Open Metrics, Events, or States when the signal name is already known.
- Test and save a query before using it in a dashboard.
- Publish a focused dashboard when a read-only link should show results without exposing the rest of the base.
Pulse must be deployed and available to your account.
Understand the Pulse model
| Resource | Responsibility |
|---|---|
| Base | Access, retention, sources, queries, and dashboards for one telemetry context |
| Source | Metrics endpoint or HTTP ingest connection |
| Resource | Stable observed object that groups its metrics, events, and states |
| Signal | Named metric, event, or state published by one or more sources |
| Query and dashboard | Reusable analysis and its operator-facing presentation |
Dimensions distinguish stable variants such as region, route, mount, or service. Put unique request IDs, sessions, full URLs, and protected details in event fields rather than metric dimensions.
How Pulse fits Cloud
Pulse owns ingestion, retention, inventory, query execution, saved queries, and dashboards inside each base. Cloud supplies identity, resource access, application routing, lifecycle hooks, and operational primitives. A source credential belongs to one source and does not grant access to unrelated bases.
Find detailed product help
Open Help inside Pulse for the data model, source health, query language, dashboard DSL, retention, public displays, and troubleshooting. Developers can read Observability, Resource authorization, and Resource API keys for adjacent Cloud contracts.
Inspect Pulse from the terminal
The native module can inspect a deployment before a script chooses a base:
cld pulse list --json
cld pulse capabilities --jsonRun cld pulse help for bases, sources, signals, queries, dashboards, and
access. Run cld pulse <command> --help before ingesting data or changing a
base.
Deployment requirements
See Deployment requirements for this app’s startup prerequisites, optional integrations, configuration and functional checks.
Pulse initializes its tables atomically in a fresh pulse schema. Later starts
reuse that installation without changing its data. Existing Alpha schemas are
rejected: there is no automatic upgrade or data repair. Before replacing a
disposable Alpha installation, explicitly remove only its Pulse schema; the
application never deletes it on startup.
Server collection before deployment
Metrics support gauges and nonnegative counters. Type and unit are fixed per metric name within a base; conflicting input rejects the entire batch. Counter rates use observed consecutive samples and correct resets before combining series. Histogram and summary aggregation is not supported.
Prometheus sources accept declared gauge/counter families and expose skipped samples in source diagnostics. Scrapes use whole-minute intervals (minimum 60 seconds), one collection timestamp, and a 15-second timeout covering lookup, connection, headers, and body. Sources with no instance, host, or node label use the endpoint host and port as their target resource.
Pulse scrapes only public endpoints by default. It refuses internal addresses,
reserved names, and redirects to them. An operator can allow internal targets
with PULSE_SCRAPE_ALLOWED_TARGETS. Exact host entries trust every address that
name resolves to; IP and CIDR entries allow addresses in that range. Reserved
names such as localhost and names under .internal require an exact host entry.
An entry covers every port and path on that host or range, and /0 entries are
refused. Scrapes request uncompressed responses and refuse a compressed body.
Existing internal sources remain stored, but scrapes fail with this reason until
the operator allows the target.
The initial operator scenario is 20–50 servers, approximately ten websites, 60-second sampling and 30-day raw retention. Website backends send events using secret source tokens. This is a test scenario, not a measured production capacity guarantee.
Retention and complete metric hours
Pulse records whether each metric hour is dirty, complete or sealed. The hourly worker resumes unfinished hours after downtime without a fixed lookback limit. Late samples and corrections inside raw retention mark the hour dirty again; queries use its raw samples until recomputation completes. Each query combines complete rollups with remaining raw samples in one database snapshot.
Raw metric retention preserves the entire boundary hour. Older raw hours are removed only after successful rollup and sealing. Sealed hours reject later writes, including after an operator extends retention: deleted samples cannot be reconstructed. Metric ingestion rejects timestamps older than raw retention. Rollup retention also applies to full hours.
Hourly rollups retain sample count, sum, minimum, maximum and the last value with its actual timestamp. They support count, sum, average, minimum, maximum and latest in whole-hour bucket sizes. Counter rate/increase and sample percentiles require raw data. Queries outside available retention fail explicitly; partial historical hours cannot be reconstructed from an hourly aggregate.
Retention also removes expired scrape history and unreferenced metric series, definitions, resources and field metadata. Current state persists until replaced or explicitly cleared, independently of history retention. A resource or field still needed by current state or retained observations remains in its catalog. Continuously creating new state identities therefore requires an explicit operator lifecycle; raw retention does not bound that inventory.
On default Timescale installations, retention also releases closed empty chunks once ordinary vacuum has emptied their heap. Each candidate is checked again under lock; chunks containing another base's data or raw samples awaiting aggregation remain intact. Maintenance yields to concurrent readers and writers and uses bounded retries. Keep autovacuum enabled; physical space reclamation is asynchronous. This cleanup covers uncompressed partitions with a single time dimension.
Website analytics from your backend
Create one HTTP ingest source per website and keep its source token in the
website backend's secret store. Send to POST /api/pulse/ingest with
Authorization: Bearer <source-token>. Do not include this token in browser
JavaScript. Token rotation keeps the same source and identity namespace.
Use page.viewed for an observed pageview and conversion.completed for an
explicit business outcome. For both, use one website resource, for example
{"type":"website","id":"example.com"}. Do not create resources for individual
requests or visitors.
| Field | Website convention |
|---|---|
dimensions.route |
Route template such as /articles/:slug, not a unique URL |
dimensions.campaign |
Optional controlled campaign name |
attributes.referrerHost |
Optional hostname, with URL parameters removed |
actorId |
Optional opaque visitor ID supplied by the backend |
sessionId |
Optional opaque session ID supplied by the backend |
sensitive |
Optional short-lived sensitive fields, under the base's separate policy |
payload |
Additional event details that should not become dimensions |
Pulse does not derive identities from IP addresses or user agents and does not create sessions. Unique counts exclude missing IDs and distinguish identical IDs from different sources, including after source deletion. A period-wide unique count is calculated over all events in that period, never by adding daily counts.
A backend delivery can use this request. Persist the body and delivery ID before sending if delivery must survive a backend restart:
const body = JSON.stringify({
events: [{
kind: "page.viewed",
ts: new Date().toISOString(),
resource: { type: "website", id: "example.com" },
dimensions: { route: "/articles/:slug" },
actorId: visitorId,
sessionId,
}],
});
const response = await fetch(`${pulseUrl}/api/pulse/ingest`, {
method: "POST",
headers: {
authorization: `Bearer ${sourceToken}`,
"content-type": "application/json",
"idempotency-key": deliveryId,
},
body,
signal: AbortSignal.timeout(15_000),
});
if (!response.ok) throw new Error(`Pulse ingest failed: ${response.status}`);On a timeout, connection failure, 429 or server error, retry the unchanged body with the same delivery ID within 24 hours, using bounded backoff in your backend queue. Other client errors require correcting the request. A correlation ID is not a deduplication key. Automatic indefinite retries and durable backend queues are not provided by this example.
Use either a relative since range or an absolute from/to pair. Absolute
values require UTC or an explicit offset. Both forms use a fixed, exclusive upper
bound: [from,to). Queries older than the base's retained history fail explicitly.
Metric historical ranges use exact UTC hours after raw data expires.
events page.viewed unique actor every all from 2026-09-01T00:00:00+02:00 to 2026-10-01T00:00:00+02:00
events page.viewed count every day timezone Europe/Berlin since 7d group by route
events conversion.completed count every month timezone Europe/Berlin since 90d
metric system.cpu.usage avg every 1h from 2026-09-01T00:00:00Z to 2026-09-02T00:00:00Zday, week and month use calendar boundaries in the required IANA time zone;
weeks begin on Monday. Daylight saving transitions can produce 23- or 25-hour
days. Duration buckets such as 1d remain fixed 24-hour UTC intervals. every all
returns one point per group for the whole selected window. An empty ungrouped
window returns zero for counts and uniques, and null for sum.