openvibers/openvibe.contracts Commits

Contracts 0.132.0: an Events position is an opaque cursor only (#85)

OP OpenVibers committed 10 Oct 2026 · 5e4baabcdd · 48 files, +308 −156

Plan T7, ADR-042 decision 7: an Events position is an opaque cursor and nothing else. `events.read-result@1` and `events.read-request@1` drop the numeric positions their own descriptions kept "only until every consumer reads by cursor".

Every consumer reads by cursor now. I checked across the estate:
- Community's relay (Community #56, deployed);
- the SDK's pull, iterate, checkpoints and realtime (openvibe-sdk 0.42.0);
- Shared's bell and release watcher (3.0.1);
- Examples (#14);
- Chat's e2e test (#64);
- Services' project archive, which already read by cursor.

**Schema changes:**
- **Pages:** `{ events: [{ seq, cursor, event }], next_cursor, latest_cursor, gap? }`.
  - `next_after_seq` and `latest_seq` are gone.
  - `next_cursor`, `latest_cursor` and each item's `cursor` are required (Events always sends them).
  - Without `after=`, a page starts at the oldest retained event; `after_seq` is refused.
- **Single event:** `cursor` is required beside `seq`.
- **Checkpoints:** `PUT { topic, cursor, carrier? }` takes an opaque cursor string; a number is refused. Answers are `{ consumer, topic, cursor, carrier, updated_at }`, with the cursor as stored (null when none is).
- **Realtime (description):** the SSE id is the cursor, and `Last-Event-ID` resumes only from a cursor.
- **Per-event `seq`:** stays, documented as informational. Deleting it, with `X-OpenVibe-Seq` and the global-order promise, is ADR-042's last step; clients already dedupe by `event_id` (SDK 0.42, Shared 3.0.1).
- **ADR-042:** records what decision 7 became.

**Fixtures:** pages and checkpoints with cursors only. Three new invalid fixtures: a numeric page, a numeric checkpoint answer, a numeric checkpoint request. A valid stored checkpoint is added.

**Compat:** `node scripts/compat.js v0.131.0` reports no breaking changes. The gate does not descend into `anyOf` schemas. The removal is the one v1 announced, and the consumer check above is what makes it safe.

`node test/run.js`: 18,388 checks passed; `generated/` is up to date.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 923d85c9..4358ae38 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -4,6 +4,26 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
++## 0.132.0 — 2026-10-10
++
++**An Events position is an opaque cursor and nothing else** (ADR-042 decision 7, plan T7). `events.read-result@1` and
++`events.read-request@1` drop the numeric positions their own descriptions kept "only until every consumer reads by
++cursor". Every consumer does now, checked across the estate: Community's relay (Community #56), the SDK's pull,
++iterate, checkpoints and realtime (openvibe-sdk 0.42.0), Shared's bell and release watcher (3.0.1), Examples (#14)
++and Chat's e2e test (#64). Services' project archive already read by cursor.
++
++- **Pages:** `GET /api/v1/events` answers `{ events: [{ seq, cursor, event }], next_cursor, latest_cursor, gap? }`.
++ `next_after_seq` and `latest_seq` are gone, and `next_cursor`, `latest_cursor` and each item's `cursor` are required
++ (Events always sent them). Without `after=`, a page starts at the oldest retained event; `after_seq` is refused.
++- **Single event:** `GET /api/v1/events/:event_id` requires `cursor` beside `seq`.
++- **Checkpoints:** `PUT` takes `{ topic, cursor, carrier? }` with `cursor` an opaque cursor string; a number is refused.
++ `GET`/`PUT` answer `{ consumer, topic, cursor, carrier, updated_at }` with the cursor as stored (null when none is).
++- **Realtime:** the SSE id is the cursor, and `Last-Event-ID` resumes only from a cursor.
++- **Per-event `seq`:** stays, documented as informational (not a position). Deleting it, with `X-OpenVibe-Seq` and the
++ global-order promise, is ADR-042's last step; clients already dedupe by `event_id`.
++- **Fixtures:** pages and checkpoints with cursors only, plus three invalid ones (a numeric page, a numeric checkpoint
++ answer, a numeric checkpoint request). ADR-042 records what decision 7 became.
++
## 0.131.0 — 2026-10-10
- `openre.resource.read` (owner `openre`, first-party, **active**): OpenRestream's resource index, its stream
ddiff --git a/contracts/events/read-request.v1.json b/contracts/events/read-request.v1.json
iindex 72b49975..725689fa 100644
---- a/contracts/events/read-request.v1.json
++++ b/contracts/events/read-request.v1.json
@@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/events/read-request.v1.json",
"title": "EventsReadRequest",
-- "description": "events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor } stores the calling consumer's cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after_seq=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body.",
++ "description": "events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor, carrier? } stores the calling consumer's opaque cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body. A position is only ever an opaque cursor (ADR-042 decision 7): after_seq is refused (400) and a numeric checkpoint cursor is refused.",
"anyOf": [
{
"type": "object",
@@@ -18,9 +18,16 @@
"description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."
},
"cursor": {
-- "type": "integer",
-- "minimum": 0,
-- "description": "A seq (next_after_seq of the last page handled)."
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "An opaque cursor: a page's next_cursor (or latest_cursor). Never a number, never parsed."
++ },
++ "carrier": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 64,
++ "description": "The carrier the position is on (ADR-042), recorded with it."
}
}
},
ddiff --git a/contracts/events/read-result.v1.json b/contracts/events/read-result.v1.json
iindex 5c10b219..5e2005ee 100644
---- a/contracts/events/read-result.v1.json
++++ b/contracts/events/read-result.v1.json
@@@ -2,14 +2,14 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/events/read-result.v1.json",
"title": "EventsReadResult",
-- "description": "events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, cursor?, event }], next_after_seq, next_cursor?, latest_seq, latest_cursor?, gap? } (keep next_cursor as the position and read on with after=: it moves past events that did not match; latest_cursor is the head, for a consumer that starts at now; the numeric after_seq, next_after_seq and latest_seq remain only until every consumer reads by cursor (plan T7); gap { from_seq, to_seq } says events after the position were already pruned by retention); GET /api/v1/events/:event_id → { seq, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, updated_at } (cursor 0 and updated_at null when none is stored). GET /realtime/stream?topics= (events.event.read) is a Server-Sent Events feed whose messages carry the same { seq, event } (id: the cursor once Events sends one, else the seq; a gap message when the cursor is older than retention). An app sees only its project's events in its token's environment plus public first-party events.",
++ "description": "events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, cursor, event }], next_cursor, latest_cursor, gap? } (keep next_cursor as the position and read on with after=: it moves past events that did not match; latest_cursor is the head, for a consumer that starts at now; without after= a page starts at the oldest retained event; gap { from_seq, to_seq } says events after the position were already pruned by retention); GET /api/v1/events/:event_id → { seq, cursor, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, carrier, updated_at } (cursor, carrier and updated_at null when none is stored). GET /realtime/stream?topics= (events.event.read) is a Server-Sent Events feed whose messages carry the same { seq, event } with the cursor as the SSE id; it resumes from Last-Event-ID (or last_event_id=) when that is a cursor, and a gap message comes first when the cursor is older than retention. A position is only ever an opaque cursor (ADR-042 decision 7): the per-event seq is informational, and nothing accepts it as a position. An app sees only its project's events in its token's environment plus public first-party events.",
"anyOf": [
{
"type": "object",
"required": [
"events",
-- "next_after_seq",
-- "latest_seq"
++ "next_cursor",
++ "latest_cursor"
],
"properties": {
"events": {
@@@ -19,12 +19,14 @@
"type": "object",
"required": [
"seq",
++ "cursor",
"event"
],
"properties": {
"seq": {
"type": "integer",
-- "minimum": 1
++ "minimum": 1,
++ "description": "Informational: the event's number in the hot store. Not a position."
},
"event": {
"$ref": "event-envelope.v1.json"
@@@ -33,19 +35,11 @@
"type": "string",
"minLength": 1,
"maxLength": 200,
-- "description": "Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it. Returned beside seq for one release; seq then goes."
++ "description": "Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it."
}
}
}
},
-- "next_after_seq": {
-- "type": "integer",
-- "minimum": 0
-- },
-- "latest_seq": {
-- "type": "integer",
-- "minimum": 0
-- },
"gap": {
"type": "object",
"required": [
@@@ -65,13 +59,13 @@
"type": "string",
"minLength": 1,
"maxLength": 200,
-- "description": "Opaque position after this page (ADR-042): pass it back as after= for the next page. Returned beside next_after_seq for one release."
++ "description": "Opaque position after this page (ADR-042): pass it back as after= for the next page."
},
"latest_cursor": {
"type": "string",
"minLength": 1,
"maxLength": 200,
-- "description": "The head as an opaque cursor (ADR-042): a consumer that starts at now stores it and reads on with after=, never needing latest_seq."
++ "description": "The head as an opaque cursor (ADR-042): a consumer that starts at now stores it and reads on with after=."
}
}
},
@@@ -79,12 +73,14 @@
"type": "object",
"required": [
"seq",
++ "cursor",
"event"
],
"properties": {
"seq": {
"type": "integer",
-- "minimum": 1
++ "minimum": 1,
++ "description": "Informational: the event's number in the hot store. Not a position."
},
"event": {
"$ref": "event-envelope.v1.json"
@@@ -93,7 +89,7 @@
"type": "string",
"minLength": 1,
"maxLength": 200,
-- "description": "Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it. Returned beside seq for one release; seq then goes."
++ "description": "Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it."
}
}
},
@@@ -113,8 +109,13 @@
"type": "string"
},
"cursor": {
-- "type": "integer",
-- "minimum": 0
++ "type": [
++ "string",
++ "null"
++ ],
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The stored opaque cursor, as it was stored; null when none is."
},
"updated_at": {
"type": [
@@@ -122,6 +123,13 @@
"null"
],
"format": "date-time"
++ },
++ "carrier": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "The carrier the stored position is on (ADR-042), or null."
}
}
}
ddiff --git a/docs/adr/ADR-042-events-fabric.md b/docs/adr/ADR-042-events-fabric.md
iindex 3c5c9d73..ad207279 100644
---- a/docs/adr/ADR-042-events-fabric.md
++++ b/docs/adr/ADR-042-events-fabric.md
@@@ -53,6 +53,11 @@ cell-local carrier). Amends ADR-004: its "Kafka/NATS now: rejected" is supersede
`next_cursor` (opaque: a hot-store position plus a retention epoch); the SSE `id` becomes the cursor. Consumers'
positions live in `consumer_checkpoints` with `epoch` and `carrier` columns. `seq` is still returned for exactly one
release after the cursor ships; it and `X-OpenVibe-Seq` are then deleted, with the "global order" promise.
++ *Positions done 2026-10-10 (Contracts 0.132.0, openvibe-sdk 0.42.0, Shared 3.0.1, Events):* every consumer reads by
++ cursor, and nothing accepts a number as a position any more: `after_seq` is a 400, pages carry no `next_after_seq` or
++ `latest_seq`, checkpoints store and return the opaque cursor, and a bare-number `Last-Event-ID` is not a position.
++ The per-event `seq` (pull items, publish results, SSE data, the delivery body and `X-OpenVibe-Seq`) remains as an
++ informational number; deleting it is the last step, and clients already dedupe by `event_id`.
8. **Three tiers.** hot = `events` (pruned at `retention.hot`); replay = `events_archive` in the same database (no
delivery rows, compressed payloads, pruned at `retention.replay`); archive = monthly NDJSON objects in object
storage, restored only by an operator job into `events_archive`. Pulls and scans span hot and replay with one
ddiff --git a/fixtures/events.read-request/invalid/numeric-cursor.json b/fixtures/events.read-request/invalid/numeric-cursor.json
nnew file mode 100644
iindex 00000000..1fefe740
---- /dev/null
++++ b/fixtures/events.read-request/invalid/numeric-cursor.json
@@@ -0,0 +1,4 @@
++{
++ "topic": "media.vod.*",
++ "cursor": 88213
++}
ddiff --git a/fixtures/events.read-request/valid/checkpoint.json b/fixtures/events.read-request/valid/checkpoint.json
iindex b5525c31..3a504d08 100644
---- a/fixtures/events.read-request/valid/checkpoint.json
++++ b/fixtures/events.read-request/valid/checkpoint.json
@@@ -1 +1,5 @@
--{ "topic": "media.vod.*", "cursor": 88213 }
++{
++ "topic": "media.vod.*",
++ "cursor": "c1.0.ODgyMTM",
++ "carrier": "pg-v1"
++}
ddiff --git a/fixtures/events.read-result/invalid/bad-envelope.json b/fixtures/events.read-result/invalid/bad-envelope.json
iindex 8e4c65f5..e61b1bb2 100644
---- a/fixtures/events.read-result/invalid/bad-envelope.json
++++ b/fixtures/events.read-result/invalid/bad-envelope.json
@@@ -1 +1,13 @@
--{ "events": [ { "seq": 1, "event": { "event_type": "x" } } ], "next_after_seq": 1, "latest_seq": 1 }
++{
++ "events": [
++ {
++ "seq": 1,
++ "cursor": "c1.0.MQ",
++ "event": {
++ "event_type": "x"
++ }
++ }
++ ],
++ "next_cursor": "c1.0.MQ",
++ "latest_cursor": "c1.0.MQ"
++}
ddiff --git a/fixtures/events.read-result/invalid/empty-latest-cursor.json b/fixtures/events.read-result/invalid/empty-latest-cursor.json
iindex abaee460..dbd5da6a 100644
---- a/fixtures/events.read-result/invalid/empty-latest-cursor.json
++++ b/fixtures/events.read-result/invalid/empty-latest-cursor.json
@@@ -24,8 +24,6 @@
"cursor": "c1.e0.1"
}
],
-- "next_after_seq": 88240,
-- "latest_seq": 88240,
"next_cursor": "c1.e0.next",
"latest_cursor": ""
}
ddiff --git a/fixtures/events.read-result/invalid/numeric-checkpoint.json b/fixtures/events.read-result/invalid/numeric-checkpoint.json
nnew file mode 100644
iindex 00000000..6349a642
---- /dev/null
++++ b/fixtures/events.read-result/invalid/numeric-checkpoint.json
@@@ -0,0 +1,6 @@
++{
++ "consumer": "live",
++ "topic": "media.vod.*",
++ "cursor": 88213,
++ "updated_at": "2026-10-10T12:00:00Z"
++}
ddiff --git a/fixtures/events.read-result/invalid/numeric-page.json b/fixtures/events.read-result/invalid/numeric-page.json
nnew file mode 100644
iindex 00000000..d1db28c7
---- /dev/null
++++ b/fixtures/events.read-result/invalid/numeric-page.json
@@@ -0,0 +1,5 @@
++{
++ "events": [],
++ "next_after_seq": 5,
++ "latest_seq": 5
++}
ddiff --git a/fixtures/events.read-result/valid/checkpoint.json b/fixtures/events.read-result/valid/checkpoint.json
nnew file mode 100644
iindex 00000000..f9b1668d
---- /dev/null
++++ b/fixtures/events.read-result/valid/checkpoint.json
@@@ -0,0 +1,7 @@
++{
++ "consumer": "live",
++ "topic": "media.vod.*",
++ "cursor": "c1.0.ODgyMTM",
++ "carrier": "pg-v1",
++ "updated_at": "2026-10-10T12:00:00Z"
++}
ddiff --git a/fixtures/events.read-result/valid/no-checkpoint.json b/fixtures/events.read-result/valid/no-checkpoint.json
iindex 9c1e8415..3887703e 100644
---- a/fixtures/events.read-result/valid/no-checkpoint.json
++++ b/fixtures/events.read-result/valid/no-checkpoint.json
@@@ -1 +1,7 @@
--{ "consumer": "live", "topic": "media.vod.*", "cursor": 0, "updated_at": null }
++{
++ "consumer": "live",
++ "topic": "media.vod.*",
++ "cursor": null,
++ "carrier": null,
++ "updated_at": null
++}
ddiff --git a/fixtures/events.read-result/valid/page-with-cursor.json b/fixtures/events.read-result/valid/page-with-cursor.json
iindex 8cee2a9c..dae94410 100644
---- a/fixtures/events.read-result/valid/page-with-cursor.json
++++ b/fixtures/events.read-result/valid/page-with-cursor.json
@@@ -24,7 +24,6 @@
"cursor": "c1.e0.1"
}
],
-- "next_after_seq": 88240,
-- "latest_seq": 88240,
-- "next_cursor": "c1.e0.next"
++ "next_cursor": "c1.e0.next",
++ "latest_cursor": "c1.0.ODgyNDA"
}
ddiff --git a/fixtures/events.read-result/valid/page-with-latest-cursor.json b/fixtures/events.read-result/valid/page-with-latest-cursor.json
iindex 982aaf25..7c6c435f 100644
---- a/fixtures/events.read-result/valid/page-with-latest-cursor.json
++++ b/fixtures/events.read-result/valid/page-with-latest-cursor.json
@@@ -24,8 +24,6 @@
"cursor": "c1.e0.1"
}
],
-- "next_after_seq": 88240,
-- "latest_seq": 88240,
"next_cursor": "c1.e0.next",
"latest_cursor": "c1.0.Nw"
}
ddiff --git a/fixtures/events.read-result/valid/page.json b/fixtures/events.read-result/valid/page.json
iindex e6e9cfd7..b7486a23 100644
---- a/fixtures/events.read-result/valid/page.json
++++ b/fixtures/events.read-result/valid/page.json
@@@ -1 +1,29 @@
--{ "events": [ { "seq": 88213, "event": { "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPR", "event_type": "app.p01jab2c3d4e5f6g7h8j9k0mnpq.order.created", "version": 1, "source": "app-01jab2c3d4e5f6g7h8j9k0mnps", "actor": { "type": "app", "id": "app_01JAB2C3D4E5F6G7H8J9K0MNPS" }, "timestamp": "2026-09-23T12:00:00Z", "visibility": "internal", "subject": { "type": "order", "id": "42" }, "payload": { "total": 3 }} } ], "next_after_seq": 88240, "latest_seq": 88240 }
++{
++ "events": [
++ {
++ "seq": 88213,
++ "event": {
++ "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPR",
++ "event_type": "app.p01jab2c3d4e5f6g7h8j9k0mnpq.order.created",
++ "version": 1,
++ "source": "app-01jab2c3d4e5f6g7h8j9k0mnps",
++ "actor": {
++ "type": "app",
++ "id": "app_01JAB2C3D4E5F6G7H8J9K0MNPS"
++ },
++ "timestamp": "2026-09-23T12:00:00Z",
++ "visibility": "internal",
++ "subject": {
++ "type": "order",
++ "id": "42"
++ },
++ "payload": {
++ "total": 3
++ }
++ },
++ "cursor": "c1.0.ODgyMTM"
++ }
++ ],
++ "next_cursor": "c1.0.ODgyNDA",
++ "latest_cursor": "c1.0.ODgyNDA"
++}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex 39a12768..b70170ee 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -6059,7 +6059,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/events/read-request.v1.json",
"title": "EventsReadRequest",
-- "description": "events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor } stores the calling consumer's cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after_seq=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body.",
++ "description": "events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor, carrier? } stores the calling consumer's opaque cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body. A position is only ever an opaque cursor (ADR-042 decision 7): after_seq is refused (400) and a numeric checkpoint cursor is refused.",
"anyOf": [
{
"type": "object",
@@@ -6075,9 +6075,16 @@
"description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."
},
"cursor": {
-- "type": "integer",
-- "minimum": 0,
-- "description": "A seq (next_after_seq of the last page handled)."
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "An opaque cursor: a page's next_cursor (or latest_cursor). Never a number, never parsed."
++ },
++ "carrier": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 64,
++ "description": "The carrier the position is on (ADR-042), recorded with it."
}
}
},
@@@ -6095,14 +6102,14 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/events/read-result.v1.json",
"title": "EventsReadResult",
-- "description": "events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, cursor?, event }], next_after_seq, next_cursor?, latest_seq, latest_cursor?, gap? } (keep next_cursor as the position and read on with after=: it moves past events that did not match; latest_cursor is the head, for a consumer that starts at now; the numeric after_seq, next_after_seq and latest_seq remain only until every consumer reads by cursor (plan T7); gap { from_seq, to_seq } says events after the position were already pruned by retention); GET /api/v1/events/:event_id → { seq, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, updated_at } (cursor 0 and updated_at null when none is stored). GET /realtime/stream?topics= (events.event.read) is a Server-Sent Events feed whose messages carry the same { seq, event } (id: the cursor once Events sends one, else the seq; a gap message when the cursor is older than retention). An app sees only its project's events in its token's environment plus public first-party events.",
++ "description": "events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, cursor, event }], next_cursor, latest_cursor, gap? } (keep next_cursor as the position and read on with after=: it moves past events that did not match; latest_cursor is the head, for a consumer that starts at now; without after= a page starts at the oldest retained event; gap { from_seq, to_seq } says events after the position were already pruned by retention); GET /api/v1/events/:event_id → { seq, cursor, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, carrier, updated_at } (cursor, carrier and updated_at null when none is stored). GET /realtime/stream?topics= (events.event.read) is a Server-Sent Events feed whose messages carry the same { seq, event } with the cursor as the SSE id; it resumes from Last-Event-ID (or last_event_id=) when that is a cursor, and a gap message comes first when the cursor is older than retention. A position is only ever an opaque cursor (ADR-042 decision 7): the per-event seq is informational, and nothing accepts it as a position. An app sees only its project's events in its token's environment plus public first-party events.",
"anyOf": [
{
"type": "object",
"required": [
"events",
-- "next_after_seq",
-- "latest_seq"
++ "next_cursor",
++ "latest_cursor"
],
"properties": {
"events": {
@@@ -6112,12 +6119,14 @@
"type": "object",
"required": [
"seq",
++ "cursor",
"event"
],
"properties": {
"seq": {
"type": "integer",
-- "minimum": 1
++ "minimum": 1,
++ "description": "Informational: the event's number in the hot store. Not a position."
},
"event": {
"$ref": "event-envelope.v1.json"
@@@ -6126,19 +6135,11 @@
"type": "string",
"minLength": 1,
"maxLength": 200,
-- "description": "Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it. Returned beside seq for one release; seq then goes."
++ "description": "Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it."
}
}
}
},
-- "next_after_seq": {
-- "type": "integer",
-- "minimum": 0
-- },
-- "latest_seq": {
-- "type": "integer",
-- "minimum": 0
-- },
"gap": {
"type": "object",
"required": [
@@@ -6158,13 +6159,13 @@
"type": "string",
"minLength": 1,
"maxLength": 200,
-- "description": "Opaque position after this page (ADR-042): pass it back as after= for the next page. Returned beside next_after_seq for one release."
++ "description": "Opaque position after this page (ADR-042): pass it back as after= for the next page."
},
"latest_cursor": {
"type": "string",
"minLength": 1,
"maxLength": 200,
-- "description": "The head as an opaque cursor (ADR-042): a consumer that starts at now stores it and reads on with after=, never needing latest_seq."
++ "description": "The head as an opaque cursor (ADR-042): a consumer that starts at now stores it and reads on with after=."
}
}
},
@@@ -6172,12 +6173,14 @@
"type": "object",
"required": [
"seq",
++ "cursor",
"event"
],
"properties": {
"seq": {
"type": "integer",
-- "minimum": 1
++ "minimum": 1,
++ "description": "Informational: the event's number in the hot store. Not a position."
},
"event": {
"$ref": "event-envelope.v1.json"
@@@ -6186,7 +6189,7 @@
"type": "string",
"minLength": 1,
"maxLength": 200,
-- "description": "Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it. Returned beside seq for one release; seq then goes."
++ "description": "Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it."
}
}
},
@@@ -6206,8 +6209,13 @@
"type": "string"
},
"cursor": {
-- "type": "integer",
-- "minimum": 0
++ "type": [
++ "string",
++ "null"
++ ],
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The stored opaque cursor, as it was stored; null when none is."
},
"updated_at": {
"type": [
@@@ -6215,6 +6223,13 @@
"null"
],
"format": "date-time"
++ },
++ "carrier": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "The carrier the stored position is on (ADR-042), or null."
}
}
}
ddiff --git a/generated/openapi/actor.json b/generated/openapi/actor.json
iindex 25947702..b26935b3 100644
---- a/generated/openapi/actor.json
++++ b/generated/openapi/actor.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Actor API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/ai.json b/generated/openapi/ai.json
iindex ae7f9f50..1f5aa23e 100644
---- a/generated/openapi/ai.json
++++ b/generated/openapi/ai.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.AI API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/billing.json b/generated/openapi/billing.json
iindex 470a3f67..3ffef4ee 100644
---- a/generated/openapi/billing.json
++++ b/generated/openapi/billing.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Billing API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/blog.json b/generated/openapi/blog.json
iindex 57268a87..aadae310 100644
---- a/generated/openapi/blog.json
++++ b/generated/openapi/blog.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Blog API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/bot.json b/generated/openapi/bot.json
iindex 204f9987..25203458 100644
---- a/generated/openapi/bot.json
++++ b/generated/openapi/bot.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Bot API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/chat.json b/generated/openapi/chat.json
iindex 81ced394..01678780 100644
---- a/generated/openapi/chat.json
++++ b/generated/openapi/chat.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Chat API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/community.json b/generated/openapi/community.json
iindex 3610e38d..1f643aef 100644
---- a/generated/openapi/community.json
++++ b/generated/openapi/community.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Community API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/coupons.json b/generated/openapi/coupons.json
iindex 38caa193..1da220bb 100644
---- a/generated/openapi/coupons.json
++++ b/generated/openapi/coupons.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Coupons API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/deals.json b/generated/openapi/deals.json
iindex ba00d774..d66b359c 100644
---- a/generated/openapi/deals.json
++++ b/generated/openapi/deals.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Deals API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/events.json b/generated/openapi/events.json
iindex a545b94a..d3583787 100644
---- a/generated/openapi/events.json
++++ b/generated/openapi/events.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Events API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
@@@ -1464,7 +1464,7 @@
},
"events.read-request.v1": {
"title": "EventsReadRequest",
-- "description": "events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor } stores the calling consumer's cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after_seq=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body.",
++ "description": "events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor, carrier? } stores the calling consumer's opaque cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body. A position is only ever an opaque cursor (ADR-042 decision 7): after_seq is refused (400) and a numeric checkpoint cursor is refused.",
"anyOf": [
{
"type": "object",
@@@ -1480,9 +1480,16 @@
"description": "Dot-separated segments of [a-z0-9_] or *, where * stands for one or more whole segments (*.* is refused)."
},
"cursor": {
-- "type": "integer",
-- "minimum": 0,
-- "description": "A seq (next_after_seq of the last page handled)."
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "An opaque cursor: a page's next_cursor (or latest_cursor). Never a number, never parsed."
++ },
++ "carrier": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 64,
++ "description": "The carrier the position is on (ADR-042), recorded with it."
}
}
},
@@@ -1493,14 +1500,14 @@
},
"events.read-result.v1": {
"title": "EventsReadResult",
-- "description": "events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, cursor?, event }], next_after_seq, next_cursor?, latest_seq, latest_cursor?, gap? } (keep next_cursor as the position and read on with after=: it moves past events that did not match; latest_cursor is the head, for a consumer that starts at now; the numeric after_seq, next_after_seq and latest_seq remain only until every consumer reads by cursor (plan T7); gap { from_seq, to_seq } says events after the position were already pruned by retention); GET /api/v1/events/:event_id → { seq, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, updated_at } (cursor 0 and updated_at null when none is stored). GET /realtime/stream?topics= (events.event.read) is a Server-Sent Events feed whose messages carry the same { seq, event } (id: the cursor once Events sends one, else the seq; a gap message when the cursor is older than retention). An app sees only its project's events in its token's environment plus public first-party events.",
++ "description": "events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, cursor, event }], next_cursor, latest_cursor, gap? } (keep next_cursor as the position and read on with after=: it moves past events that did not match; latest_cursor is the head, for a consumer that starts at now; without after= a page starts at the oldest retained event; gap { from_seq, to_seq } says events after the position were already pruned by retention); GET /api/v1/events/:event_id → { seq, cursor, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, carrier, updated_at } (cursor, carrier and updated_at null when none is stored). GET /realtime/stream?topics= (events.event.read) is a Server-Sent Events feed whose messages carry the same { seq, event } with the cursor as the SSE id; it resumes from Last-Event-ID (or last_event_id=) when that is a cursor, and a gap message comes first when the cursor is older than retention. A position is only ever an opaque cursor (ADR-042 decision 7): the per-event seq is informational, and nothing accepts it as a position. An app sees only its project's events in its token's environment plus public first-party events.",
"anyOf": [
{
"type": "object",
"required": [
"events",
-- "next_after_seq",
-- "latest_seq"
++ "next_cursor",
++ "latest_cursor"
],
"properties": {
"events": {
@@@ -1510,12 +1517,14 @@
"type": "object",
"required": [
"seq",
++ "cursor",
"event"
],
"properties": {
"seq": {
"type": "integer",
-- "minimum": 1
++ "minimum": 1,
++ "description": "Informational: the event's number in the hot store. Not a position."
},
"event": {
"$ref": "#/components/schemas/events.event-envelope.v1"
@@@ -1524,19 +1533,11 @@
"type": "string",
"minLength": 1,
"maxLength": 200,
-- "description": "Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it. Returned beside seq for one release; seq then goes."
++ "description": "Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it."
}
}
}
},
-- "next_after_seq": {
-- "type": "integer",
-- "minimum": 0
-- },
-- "latest_seq": {
-- "type": "integer",
-- "minimum": 0
-- },
"gap": {
"type": "object",
"required": [
@@@ -1556,13 +1557,13 @@
"type": "string",
"minLength": 1,
"maxLength": 200,
-- "description": "Opaque position after this page (ADR-042): pass it back as after= for the next page. Returned beside next_after_seq for one release."
++ "description": "Opaque position after this page (ADR-042): pass it back as after= for the next page."
},
"latest_cursor": {
"type": "string",
"minLength": 1,
"maxLength": 200,
-- "description": "The head as an opaque cursor (ADR-042): a consumer that starts at now stores it and reads on with after=, never needing latest_seq."
++ "description": "The head as an opaque cursor (ADR-042): a consumer that starts at now stores it and reads on with after=."
}
}
},
@@@ -1570,12 +1571,14 @@
"type": "object",
"required": [
"seq",
++ "cursor",
"event"
],
"properties": {
"seq": {
"type": "integer",
-- "minimum": 1
++ "minimum": 1,
++ "description": "Informational: the event's number in the hot store. Not a position."
},
"event": {
"$ref": "#/components/schemas/events.event-envelope.v1"
@@@ -1584,7 +1587,7 @@
"type": "string",
"minLength": 1,
"maxLength": 200,
-- "description": "Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it. Returned beside seq for one release; seq then goes."
++ "description": "Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it."
}
}
},
@@@ -1604,8 +1607,13 @@
"type": "string"
},
"cursor": {
-- "type": "integer",
-- "minimum": 0
++ "type": [
++ "string",
++ "null"
++ ],
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The stored opaque cursor, as it was stored; null when none is."
},
"updated_at": {
"type": [
@@@ -1613,6 +1621,13 @@
"null"
],
"format": "date-time"
++ },
++ "carrier": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "The carrier the stored position is on (ADR-042), or null."
}
}
}
ddiff --git a/generated/openapi/food.json b/generated/openapi/food.json
iindex e861771a..31034251 100644
---- a/generated/openapi/food.json
++++ b/generated/openapi/food.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Food API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/games.json b/generated/openapi/games.json
iindex 6c044e67..abc3352b 100644
---- a/generated/openapi/games.json
++++ b/generated/openapi/games.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Games API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/host.json b/generated/openapi/host.json
iindex 1b873acb..cb353c0f 100644
---- a/generated/openapi/host.json
++++ b/generated/openapi/host.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Host API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/inventory.json b/generated/openapi/inventory.json
iindex f5584406..204ee61b 100644
---- a/generated/openapi/inventory.json
++++ b/generated/openapi/inventory.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Inventory API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/live.json b/generated/openapi/live.json
iindex 95c92ee6..f23cbbf0 100644
---- a/generated/openapi/live.json
++++ b/generated/openapi/live.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Live API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/media.json b/generated/openapi/media.json
iindex 973b8b21..45e913d4 100644
---- a/generated/openapi/media.json
++++ b/generated/openapi/media.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Media API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/network.json b/generated/openapi/network.json
iindex 17d61031..94287b8d 100644
---- a/generated/openapi/network.json
++++ b/generated/openapi/network.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Network API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/news.json b/generated/openapi/news.json
iindex 8a0c2cf4..7d64672c 100644
---- a/generated/openapi/news.json
++++ b/generated/openapi/news.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.News API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/openre.json b/generated/openapi/openre.json
iindex 9efb0468..22857bbe 100644
---- a/generated/openapi/openre.json
++++ b/generated/openapi/openre.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenRestream API",
-- "version": "0.131.0",
-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/reviews.json b/generated/openapi/reviews.json
iindex 16c2fac3..b7656eb7 100644
---- a/generated/openapi/reviews.json
++++ b/generated/openapi/reviews.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Reviews API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/run.json b/generated/openapi/run.json
iindex d08bdf41..a1ccf18f 100644
---- a/generated/openapi/run.json
++++ b/generated/openapi/run.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Run API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/search.json b/generated/openapi/search.json
iindex e8020060..3c8917c2 100644
---- a/generated/openapi/search.json
++++ b/generated/openapi/search.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Search API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/services.json b/generated/openapi/services.json
iindex e15d3afe..bc035362 100644
---- a/generated/openapi/services.json
++++ b/generated/openapi/services.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Services API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/sources.json b/generated/openapi/sources.json
iindex a5862d0a..c7f65730 100644
---- a/generated/openapi/sources.json
++++ b/generated/openapi/sources.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Sources API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/tips.json b/generated/openapi/tips.json
iindex 4e4c71e9..cceec1ba 100644
---- a/generated/openapi/tips.json
++++ b/generated/openapi/tips.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tips API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/tools.json b/generated/openapi/tools.json
iindex b09fcc43..1a9ff2e0 100644
---- a/generated/openapi/tools.json
++++ b/generated/openapi/tools.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tools API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/trade.json b/generated/openapi/trade.json
iindex 01cc9682..d05e42f9 100644
---- a/generated/openapi/trade.json
++++ b/generated/openapi/trade.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Trade API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/vip.json b/generated/openapi/vip.json
iindex 0af34a7f..9096a40e 100644
---- a/generated/openapi/vip.json
++++ b/generated/openapi/vip.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.VIP API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/watch.json b/generated/openapi/watch.json
iindex 181e69b9..00ead42c 100644
---- a/generated/openapi/watch.json
++++ b/generated/openapi/watch.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Watch API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/wiki.json b/generated/openapi/wiki.json
iindex 38f7f623..4317594e 100644
---- a/generated/openapi/wiki.json
++++ b/generated/openapi/wiki.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Wiki API",
-- "version": "0.131.0",
-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.131.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.132.0",
++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.132.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex 3280d8b3..ed610877 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -11186,7 +11186,7 @@ export type EventsPublishResult =
/** [email protected] (owner: events) */
/**
-- * events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor } stores the calling consumer's cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after_seq=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body.
++ * events.read-request@1: request bodies of the pull API on OpenVibe.Events (events.event.read, events.app.read). PUT /api/v1/checkpoints: { topic, cursor, carrier? } stores the calling consumer's opaque cursor for a topic pattern (an app's patterns must start with a literal segment and app.* must name its own project_key). GET /api/v1/events?topic=&after=&limit=, GET /api/v1/events/:event_id, GET /api/v1/checkpoints?topic= and GET /realtime/stream?topics= take no body. A position is only ever an opaque cursor (ADR-042 decision 7): after_seq is refused (400) and a numeric checkpoint cursor is refused.
*/
export type EventsReadRequest =
| {
@@@ -11195,15 +11195,19 @@ export type EventsReadRequest =
*/
topic: string;
/**
-- * A seq (next_after_seq of the last page handled).
++ * An opaque cursor: a page's next_cursor (or latest_cursor). Never a number, never parsed.
*/
-- cursor: number;
++ cursor: string;
++ /**
++ * The carrier the position is on (ADR-042), recorded with it.
++ */
++ carrier?: string;
}
| NoBody;
/** [email protected] (owner: events) */
/**
-- * events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, cursor?, event }], next_after_seq, next_cursor?, latest_seq, latest_cursor?, gap? } (keep next_cursor as the position and read on with after=: it moves past events that did not match; latest_cursor is the head, for a consumer that starts at now; the numeric after_seq, next_after_seq and latest_seq remain only until every consumer reads by cursor (plan T7); gap { from_seq, to_seq } says events after the position were already pruned by retention); GET /api/v1/events/:event_id → { seq, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, updated_at } (cursor 0 and updated_at null when none is stored). GET /realtime/stream?topics= (events.event.read) is a Server-Sent Events feed whose messages carry the same { seq, event } (id: the cursor once Events sends one, else the seq; a gap message when the cursor is older than retention). An app sees only its project's events in its token's environment plus public first-party events.
++ * events.read-result@1: answers of the pull API on OpenVibe.Events. GET /api/v1/events → { events: [{ seq, cursor, event }], next_cursor, latest_cursor, gap? } (keep next_cursor as the position and read on with after=: it moves past events that did not match; latest_cursor is the head, for a consumer that starts at now; without after= a page starts at the oldest retained event; gap { from_seq, to_seq } says events after the position were already pruned by retention); GET /api/v1/events/:event_id → { seq, cursor, event }; GET|PUT /api/v1/checkpoints → { consumer, topic, cursor, carrier, updated_at } (cursor, carrier and updated_at null when none is stored). GET /realtime/stream?topics= (events.event.read) is a Server-Sent Events feed whose messages carry the same { seq, event } with the cursor as the SSE id; it resumes from Last-Event-ID (or last_event_id=) when that is a cursor, and a gap message comes first when the cursor is older than retention. A position is only ever an opaque cursor (ADR-042 decision 7): the per-event seq is informational, and nothing accepts it as a position. An app sees only its project's events in its token's environment plus public first-party events.
*/
export type EventsReadResult =
| {
@@@ -11211,41 +11215,52 @@ export type EventsReadResult =
* @maxItems 1000
*/
events: {
++ /**
++ * Informational: the event's number in the hot store. Not a position.
++ */
seq: number;
event: EventEnvelope;
/**
-- * Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it. Returned beside seq for one release; seq then goes.
++ * Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it.
*/
-- cursor?: string;
++ cursor: string;
}[];
-- next_after_seq: number;
-- latest_seq: number;
gap?: {
from_seq: number;
to_seq: number;
};
/**
-- * Opaque position after this page (ADR-042): pass it back as after= for the next page. Returned beside next_after_seq for one release.
++ * Opaque position after this page (ADR-042): pass it back as after= for the next page.
*/
-- next_cursor?: string;
++ next_cursor: string;
/**
-- * The head as an opaque cursor (ADR-042): a consumer that starts at now stores it and reads on with after=, never needing latest_seq.
++ * The head as an opaque cursor (ADR-042): a consumer that starts at now stores it and reads on with after=.
*/
-- latest_cursor?: string;
++ latest_cursor: string;
}
| {
++ /**
++ * Informational: the event's number in the hot store. Not a position.
++ */
seq: number;
event: EventEnvelope;
/**
-- * Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it. Returned beside seq for one release; seq then goes.
++ * Opaque position (ADR-042): hand it back as after= to read on from here, or as Last-Event-ID; never parse it.
*/
-- cursor?: string;
++ cursor: string;
}
| {
consumer: string;
topic: string;
-- cursor: number;
++ /**
++ * The stored opaque cursor, as it was stored; null when none is.
++ */
++ cursor: string | null;
updated_at: string | null;
++ /**
++ * The carrier the stored position is on (ADR-042), or null.
++ */
++ carrier?: string | null;
};
/** [email protected] (owner: events) */
ddiff --git a/package.json b/package.json
iindex 119def98..06bc4e38 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.131.0",
++ "version": "0.132.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"types": "generated/typescript/index.d.ts",