openvibers/openvibe.events Commits

Links: the developer console (projects, usage, the limits page) is on openvibe.services now. (#27)

OP OpenVibers committed 8 Oct 2026 · 58f6afbb99 · 3 files, +4 −4

OpenVibe.Codes became the coding-agent harness on 2026-10-08 and its console moved to OpenVibe.Services; the old
addresses redirect, but the docs and comments here now name the new home.


Claude-Session: https://claude.ai/code/session_01K8AYn3FF3HE7CPfM262qdh

Co-authored-by: Claude Opus 5.5 <[email protected]>

Changes

ddiff --git a/.env.example b/.env.example
iindex d819904..340a74c 100644
---- a/.env.example
++++ b/.env.example
@@@ -106,7 +106,7 @@ EVENTS_RECEIPT_RETENTION_DAYS=90
# Project usage (WS-N task 4): each closed hour of a developer project's publishing and webhook
# deliveries becomes one events.usage.recorded event per capability and environment, delivered to
--# Network (the project dashboard on openvibe.codes). off = nothing counted or sent.
++# Network (the project dashboard on openvibe.services). off = nothing counted or sent.
#EVENTS_USAGE=on
#EVENTS_USAGE_FLUSH_MS=300000
ddiff --git a/README.md b/README.md
iindex 3b2e381..838a6c6 100644
---- a/README.md
++++ b/README.md
@@@ -24,7 +24,7 @@ Node 22 in production (`fnm exec --using=22.22.1 npm test`). Production: `/opt/o
`GET /api/health` is liveness. `GET /api/ready` (openvibe-shared/ready) is 200 only when every required check passes — `db` (a real query), `network_jwks` (the Network signing key has loaded; it retries every 30 s while Network boots) and `delivery_worker` (when `EVENTS_WORKER` is on) — and 503 otherwise, with `failed: [...]`. The optional `dlq` check fails once more than `EVENTS_DLQ_DEGRADED_AT` (default 100) deliveries are dead: the service stays ready (200) and reports `status: "degraded"`, `degraded: ["dlq"]`. Each check carries `status`, `required`, `latency_ms`, `checked_at` and, for `dlq`, `detail: { depth, threshold }`; `latest_seq`, `deliveries`, `worker` and `realtime_connections` are still in the body. **Shape change (Track O):** `checks` used to be booleans (`{ db, worker, key }`); they are now objects keyed `db`, `network_jwks`, `delivery_worker`, `dlq`.
--`GET /metrics` (Prometheus text, direct loopback callers only — a request carrying `X-Forwarded-For` gets 404, and nginx refuses the path): HTTP golden signals by route template (`http_requests_total`, `http_request_duration_seconds`, `http_requests_in_flight`; SSE sessions excluded), process metrics, `release_info`, and `events_deliveries{status}`, `events_dlq_depth`, `events_latest_seq`, `events_realtime_connections`, `events_delivery_latency_seconds{priority}` (acceptance → successful delivery, retries included), `events_delivery_attempts_total{outcome}`. `GET /release.json` is the deployed release (ADR-016, `release.mount` from openvibe-shared 1.5.0, with the manifest 1.1.0 fields under openvibe-contracts 0.32.0); its `POST /release-metrics` would count open tabs' update reports as `release_client_updates_total`, but Events has no pages and nginx does not proxy that path. `GET /limits.json` lists the developer limits enforced here (per project and environment), read from the running config; [openvibe.codes/docs/limits](https://openvibe.codes/docs/limits) renders it.
++`GET /metrics` (Prometheus text, direct loopback callers only — a request carrying `X-Forwarded-For` gets 404, and nginx refuses the path): HTTP golden signals by route template (`http_requests_total`, `http_request_duration_seconds`, `http_requests_in_flight`; SSE sessions excluded), process metrics, `release_info`, and `events_deliveries{status}`, `events_dlq_depth`, `events_latest_seq`, `events_realtime_connections`, `events_delivery_latency_seconds{priority}` (acceptance → successful delivery, retries included), `events_delivery_attempts_total{outcome}`. `GET /release.json` is the deployed release (ADR-016, `release.mount` from openvibe-shared 1.5.0, with the manifest 1.1.0 fields under openvibe-contracts 0.32.0); its `POST /release-metrics` would count open tabs' update reports as `release_client_updates_total`, but Events has no pages and nginx does not proxy that path. `GET /limits.json` lists the developer limits enforced here (per project and environment), read from the running config; [openvibe.services/docs/limits](https://openvibe.services/docs/limits) renders it.
## Auth
@@@ -213,7 +213,7 @@ Quotas recorded in Network (`dev_quotas`) are not read yet: that needs `network.
`EVENTS_APPS=off` turns the developer-app paths off (app tokens are then judged like any other token and refused).
--**Usage** ([server/usage.js](server/usage.js), roadmap WS-N task 4). Each project's use is counted per environment and UTC hour in the transaction that does the work: `events.app.publish` in `events` (events stored; a repeated `event_id` is not counted) with every refused publish request as an error (its problem code, status, trace id and, when one event was refused, its `event_id`), and `events.app.subscribe` in `deliveries` (webhook attempts to the project's subscriptions; one without a 2xx is an error, `events.delivery.http_<status>`, `events.delivery.timeout` or `events.delivery.failed`, with the event id and its trace id). A minute after an hour closes, each rollup is stored once as an `events.usage.recorded` event (source `events`, subject the project, visibility `internal`, payload `common.usage-recorded@1` from openvibe-contracts 0.63.0) in the transaction that marks it sent, so Events is its own outbox; first-party subscribers get it like any event, apps never see it. OpenVibe.Network subscribes to it for the project dashboard on openvibe.codes. `EVENTS_USAGE=off` counts nothing; `EVENTS_USAGE_FLUSH_MS` (300000) is how often closed hours are looked for. Sent rollups are kept 7 days in `app_usage`.
++**Usage** ([server/usage.js](server/usage.js), roadmap WS-N task 4). Each project's use is counted per environment and UTC hour in the transaction that does the work: `events.app.publish` in `events` (events stored; a repeated `event_id` is not counted) with every refused publish request as an error (its problem code, status, trace id and, when one event was refused, its `event_id`), and `events.app.subscribe` in `deliveries` (webhook attempts to the project's subscriptions; one without a 2xx is an error, `events.delivery.http_<status>`, `events.delivery.timeout` or `events.delivery.failed`, with the event id and its trace id). A minute after an hour closes, each rollup is stored once as an `events.usage.recorded` event (source `events`, subject the project, visibility `internal`, payload `common.usage-recorded@1` from openvibe-contracts 0.63.0) in the transaction that marks it sent, so Events is its own outbox; first-party subscribers get it like any event, apps never see it. OpenVibe.Network subscribes to it for the project dashboard on openvibe.services. `EVENTS_USAGE=off` counts nothing; `EVENTS_USAGE_FLUSH_MS` (300000) is how often closed hours are looked for. Sent rollups are kept 7 days in `app_usage`.
**Billing readings** ([server/billing.js](server/billing.js), plan T5 step 14; [docs/cutover-events-billing-readings.md](docs/cutover-events-billing-readings.md)). Each closed UTC hour, one aggregate query turns a production project's queue operations (each publish and each delivered webhook counts `ceil(bytes / 65536)`, at least 1) into one `platform.usage-sample@1` reading, metric `queue-operation-64kb`, `idempotency_key` `events:queue-operation-64kb:<project>:<hour>`, stored once in `billing_readings` and never edited, then posted to Billing (`billing.usage.record`, `POST /api/v1/usage`) with Events' Network service token. Off by default: `EVENTS_BILLING_INTERVAL_MS` (0) turns the loop on, `OV_BILLING_INTERNAL_URL` and `OV_OAUTH_CLIENT_SECRET` let it send (unset: readings stay queued). Sandbox and first-party traffic are not billed.
ddiff --git a/server/limits.js b/server/limits.js
iindex 4cb3dee..7e7085d 100644
---- a/server/limits.js
++++ b/server/limits.js
@@@ -1,7 +1,7 @@
'use strict';
/**
* GET /limits.json (roadmap WS-N task 7): the limits a developer project meets here, read from the
-- * running configuration, so OpenVibe.Codes' limits page (openvibe.codes/docs/limits) shows what is
++ * running configuration, so OpenVibe.Services' limits page (openvibe.services/docs/limits) shows what is
* enforced and never restates it. Public: nothing in it is per project or secret.
*
* limits[] id, label, capability (the grant it bounds; null for the anonymous realtime stream),