Local-first telemetry collector + store for the Diwata stack.
Project description
Pulse
Status: v0.1.0 in progress — collector + store + query CLI/JSON API + read-only dashboard are built (Phases 1–4); see
docs/working/backlog.md. Role (architecture): Event topology backbone / "System events + telemetry" source of truth for the Diwata stack. Stack: Python 3.12+, FastAPI, SQLite (v0.1.0) → Postgres + Redis (later). Dashboard is a static vanilla-JS page served by Pulse (no node/build step).
Thesis
Pulse is the telemetry and observability layer for the Diwata stack. Every product already
emits, or will emit, small typed events at meaningful moments — Grain emits phase.close,
task.close, suggest.accept, and workflow.next.stop_reason today; Assay will emit
verification.completed and check-level results next. Pulse is the place those events land,
are stored durably, and can be queried as metrics — so the stack can answer "how is the
workflow actually performing, across products, over time?" without each product re-inventing
storage and aggregation. Like the rest of the stack, Pulse is local-first, opt-in, and
provider-neutral: nothing is collected unless explicitly enabled, everything runs against a
local SQLite file before any hosted backend exists, and the ingest contract is a plain
versioned JSON event that any producer (or agent/familiar) can POST or drop on disk — no
vendor SDK, no proprietary wire format.
Pulse does not change how producers emit. Grain's telemetry_service.emit already POSTs to a
configured endpoint and falls back to .grain/telemetry_queue.jsonl when no endpoint is
reachable. Pulse simply is that endpoint (and the drain for those queue files). The
contract Grain shipped in v0.4.0 — TelemetryEvent {event_type, version, timestamp, payload} —
is the contract Pulse accepts, unchanged.
What Pulse is
- The collector — an HTTP endpoint (and a file-drop drain) that accepts versioned
TelemetryEventJSON from any Diwata producer, exactly in Grain's shipped shape. - The store — an append-only, durable record of every accepted event. Starts as a single local SQLite file; promotes to Postgres when a hosted deployment needs it.
- The metrics surface — a CLI (
pulse ...) and JSON API that read events back as aggregates: event counts, per-type/per-product/per-phase rollups, stop-reason frequency, verification pass rates — sograin metricsand Assay reports can read cross-run history from Pulse instead of only local files. - Agent-usable / agent-agnostic — a familiar can push events and query metrics headlessly
over HTTP or the CLI with
--format json. No interactive step, no browser, no SDK lock-in. - The dashboard — a read-only, local-first human view over that same read API, served
by Pulse itself at
GET /(pulse serveserves it). A self-contained static page (vanilla JS, no build step) that renders the metrics as plain tables + inline bars — see below. - Opt-in and local-first — default off at the producer (Grain's gate) and self-hostable on a developer's own machine. Nothing leaves the machine unless an endpoint is configured.
What Pulse is NOT
- Not a producer. Pulse never instruments anyone's code. Producers own emission; Pulse owns transport, storage, and aggregation. (This is the boundary Grain's v0.4.0 contract states explicitly: "Grain only emits.")
- Not a tracer / APM. No distributed tracing, spans, flame graphs, or per-request latency profiling. Pulse ingests discrete domain events, not call stacks.
- Not an error tracker. It is not Sentry. Crash/exception capture is out of scope; events are intentional, typed, no-PII domain signals.
- Not Chronicle. Chronicle is the immutable decision + rationale + high-risk action audit trail. Pulse is operational telemetry (counts, durations, frequencies). They are distinct source-of-truth owners in the architecture.
- Not Lore. Pulse does not store normalized entity data (persons, companies, signals). It stores events about how the systems run.
- Not a vendor-locked telemetry SaaS. No proprietary wire format, no required client
library. The contract is plain versioned JSON over HTTP or a
.jsonlfile drop.
Where Pulse fits
| Layer | Owner |
|---|---|
Workflow events (phase.close, task.close, suggest.accept, workflow.next.stop_reason) |
Grain emits |
Verification events (verification.completed, check results) |
Assay emits (planned) |
| Transport · storage · aggregation · query | Pulse owns |
| System events + telemetry (source of truth) | Pulse (per docs/canonical/architecture.md) |
See docs/working/v0.1.0_plan.md for the v0.1.0 scope, docs/working/integration_grain_assay.md
for the producer-side contract, and docs/working/backlog.md for the phased build order.
Dashboard
Pulse ships a read-only dashboard — the human view over the exact same read API that
agents/familiars consume as JSON. It is a self-contained static page (vanilla JS + CSS, no
node, no build step, no framework); it only fetches the existing read endpoints and renders
them as plain tables with simple inline bars (no chart library). It adds no business logic —
every number comes from GET /v1/metrics/* and GET /v1/events.
It is served by Pulse itself, so running the collector serves the dashboard:
pulse serve # collector + dashboard at http://127.0.0.1:8006/
pulse dashboard # print the dashboard URL (convenience over `pulse serve`)
pulse dashboard --open # ...and open it in the default browser
Open http://127.0.0.1:8006/ to see:
- Overview — event counts by type and by source (
/v1/metrics/counts). - Phases — per-phase rollup: closes, tasks done, task closes, last-closed (
/v1/metrics/phases). - Stop reasons —
workflow.next.stop_reasonfrequency (/v1/metrics/stop-reasons). - Verification — Assay pass-rate, per-target trends, and over-time (
/v1/metrics/verification). Lights up automatically once Assay emitsverification.completed; shows an empty state until then. - Recent events — the newest events (
/v1/events?limit=).
The dashboard is read-only and, like the other read paths, stays open even when an ingest
token is set (PULSE_INGEST_TOKEN gates only ingest). The JSON API is unchanged — the
dashboard mounts at / (+ /dashboard.css, /dashboard.js, /static/) without touching any
/v1/* route. Empty/zero states render gracefully against a fresh store; an optional
auto-refresh toggle is off by default.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file diwata_pulse_kit-0.1.1.tar.gz.
File metadata
- Download URL: diwata_pulse_kit-0.1.1.tar.gz
- Upload date:
- Size: 59.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de94561778696d95b0d54ec50e303e4c436d0c65b74bfacf9435aff21bb29da4
|
|
| MD5 |
637c371bb891e6b29eb1835e136970e8
|
|
| BLAKE2b-256 |
eaddb0c3b230daf3fc09b663f95cfc68cea869003e9deee4ffdf8399f77a3a3a
|
File details
Details for the file diwata_pulse_kit-0.1.1-py3-none-any.whl.
File metadata
- Download URL: diwata_pulse_kit-0.1.1-py3-none-any.whl
- Upload date:
- Size: 61.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
decc46ef91951e110126e01a5f7a7982b25c75dff638fd45fbe0b352f87e2db0
|
|
| MD5 |
198e7007d34f08bf7dadd4ad2a7dbcfd
|
|
| BLAKE2b-256 |
d2d1507aa47c053cfadf2ac47871d798300d56bef34cd902c159855179e275bd
|