openvibers/openvibe.contracts Commits
v0.65.0: the follow graph on Network (roadmap WS-E task 4, ADR-030). Adds the events network.follow.created and network.follow.deleted (follower, target_type channel, target_id the channel owner's subject, notify flags, per-pair revision; subject visibility), network.follow-status-result@1 (public count plus the caller's own follow), network.follow-list-result@1 (a page of follows, never public) and the capability network.follows.read (first-party, never for apps).
Changes
@@@ -6,6 +6,13 @@ the notes were in the tag and commit messages (`git tag -n1`).## Unreleased++## 0.65.0 — 2026-09-26++++**The follow graph on Network** (roadmap WS-E task 4, ADR-030).++- **New event types** `network.follow.created` and `network.follow.deleted` (subject visibility for the follower). Their payloads carry `follower`, `target_type` (`channel` today: a Live channel named by its owner's subject), `target_id`, the notify flags, a per-pair `revision`, and for a delete a `reason`. Products keep projections they can rebuild.++- **New schemas** `network.follow-status-result@1` (the public follower count, plus the caller's own follow when signed in) and `network.follow-list-result@1` (a page of follows, never public).++- **New capability** `network.follows.read` (first-party, never for apps): who follows a target, for notifications and projections.++## 0.64.0 — 2026-09-26**Wiki VIP spaces and pages** (roadmap WS-K task 8).@@@ -2406,6 +2406,26 @@"compatibility": "backward","status": "active"},++ {++ "id": "network.follow.created",++ "version": "1.0.0",++ "owner": "network",++ "schema": "events/payloads/network.follow.created.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active",++ "adr": "ADR-030"++ },++ {++ "id": "network.follow.deleted",++ "version": "1.0.0",++ "owner": "network",++ "schema": "events/payloads/network.follow.deleted.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active",++ "adr": "ADR-030"++ },{"id": "network.block.changed","version": "1.0.0",@@@ -2515,6 +2535,26 @@"compatibility": "backward","status": "active"},++ {++ "id": "network.follow-list-result",++ "version": "1.0.0",++ "owner": "network",++ "schema": "network/follow-list-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active",++ "adr": "ADR-030"++ },++ {++ "id": "network.follow-status-result",++ "version": "1.0.0",++ "owner": "network",++ "schema": "network/follow-status-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active",++ "adr": "ADR-030"++ },{"id": "network.operator-alerts-request","version": "1.0.0",nnew file mode 100644@@@ -0,0 +1,18 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/network.follow.created.v1.json",++ "title": "NetworkFollowCreatedPayload",++ "description": "network.follow.created v1 (OpenVibe.Network; roadmap WS-E task 4, ADR-030). A person followed something: today a Live channel, whose target id is the channel owner's subject. Follows are the network's, kept by Network and keyed by subjects; products such as Live keep a projection they can rebuild. Written to Network's outbox in the transaction that stores the follow. A consumer applies the latest revision per (follower, target) and ignores an older one. Following again what is already followed changes nothing and sends nothing. Envelope: subject { type: user, id: <follower> }, visibility subject, actor the follower.",++ "type": "object",++ "required": ["follower", "target_type", "target_id", "revision"],++ "additionalProperties": false,++ "properties": {++ "follower": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "Who follows." },++ "target_type": { "enum": ["channel"], "description": "What kind of thing is followed. channel: a Live channel, named by its owner's subject." },++ "target_id": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The followed target: for a channel, the channel owner's subject." },++ "notify_email": { "type": "boolean" },++ "notify_push": { "type": "boolean" },++ "revision": { "type": "integer", "minimum": 1, "maximum": 9007199254740991, "description": "Grows with every change of this pair." },++ "at": { "type": "string", "format": "date-time" }++ }++}nnew file mode 100644@@@ -0,0 +1,17 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/network.follow.deleted.v1.json",++ "title": "NetworkFollowDeletedPayload",++ "description": "network.follow.deleted v1 (OpenVibe.Network; roadmap WS-E task 4, ADR-030). A person stopped following something (or the follow went with an account). Written to Network's outbox in the transaction that removes the follow; unfollowing what is not followed sends nothing. A consumer applies the latest revision per (follower, target). Envelope: subject { type: user, id: <follower> }, visibility subject, actor the follower (system:network for an account removal).",++ "type": "object",++ "required": ["follower", "target_type", "target_id", "revision"],++ "additionalProperties": false,++ "properties": {++ "follower": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" },++ "target_type": { "enum": ["channel"] },++ "target_id": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" },++ "reason": { "enum": ["unfollowed", "account_removed", "target_removed"], "description": "Why the follow went (default unfollowed)." },++ "revision": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 },++ "at": { "type": "string", "format": "date-time" }++ }++}nnew file mode 100644@@@ -0,0 +1,29 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/network/follow-list-result.v1.json",++ "title": "NetworkFollowListResult",++ "description": "network.follow-list-result@1 (ADR-030): a page of follows on OpenVibe.Network, newest first. GET /api/v1/me/follows (what the caller follows), GET /api/v1/follows/:type/:target/followers (who follows a target: the target's owner, or a service with network.follows.read). Lists are never public; counts are.",++ "type": "object",++ "required": ["items", "next_cursor"],++ "additionalProperties": false,++ "properties": {++ "items": {++ "type": "array",++ "maxItems": 500,++ "items": {++ "type": "object",++ "required": ["follower", "target_type", "target_id", "created_at"],++ "additionalProperties": false,++ "properties": {++ "follower": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" },++ "target_type": { "enum": ["channel"] },++ "target_id": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" },++ "notify_email": { "type": "boolean" },++ "notify_push": { "type": "boolean" },++ "created_at": { "type": "string", "format": "date-time" }++ }++ }++ },++ "next_cursor": { "type": ["string", "null"], "maxLength": 200 }++ }++}nnew file mode 100644@@@ -0,0 +1,18 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/network/follow-status-result.v1.json",++ "title": "NetworkFollowStatusResult",++ "description": "network.follow-status-result@1 (ADR-030): GET /api/v1/follows/:type/:target on OpenVibe.Network: the public follower count of a target, and, for a signed-in caller, whether they follow it and how they are notified. PUT and DELETE /api/v1/me/follows/:type/:target answer the same document after the change (idempotent: repeating either changes nothing).",++ "type": "object",++ "required": ["target_type", "target_id", "followers"],++ "additionalProperties": false,++ "properties": {++ "target_type": { "enum": ["channel"] },++ "target_id": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" },++ "followers": { "type": "integer", "minimum": 0 },++ "following": { "type": "boolean", "description": "Present for a signed-in caller." },++ "notify_email": { "type": "boolean" },++ "notify_push": { "type": "boolean" },++ "since": { "type": "string", "format": "date-time", "description": "When the caller started following." }++ }++}nnew file mode 100644@@@ -0,0 +1 @@++{ "items": [ { "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "created_at": "2026-09-20T10:00:00Z", "username": "alice" } ], "next_cursor": null }nnew file mode 100644@@@ -0,0 +1 @@++{ "items": [ { "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "notify_email": true, "notify_push": true, "created_at": "2026-09-20T10:00:00Z" } ], "next_cursor": null }nnew file mode 100644@@@ -0,0 +1 @@++{ "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "followers": -1 }nnew file mode 100644@@@ -0,0 +1 @@++{ "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "followers": 12 }nnew file mode 100644@@@ -0,0 +1 @@++{ "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "followers": 12, "following": true, "notify_email": false, "notify_push": true, "since": "2026-09-20T10:00:00Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "follower": "42", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "revision": 1 }nnew file mode 100644@@@ -0,0 +1 @@++{ "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "streamer", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "revision": 1 }nnew file mode 100644@@@ -0,0 +1 @@++{ "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "notify_email": true, "notify_push": true, "revision": 1, "at": "2026-09-26T16:00:00Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "revision": 1 }nnew file mode 100644@@@ -0,0 +1 @@++{ "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "revision": 0 }nnew file mode 100644@@@ -0,0 +1 @@++{ "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "reason": "unfollowed", "revision": 2, "at": "2026-09-26T17:00:00Z" }@@@ -25347,6 +25347,110 @@}}},++ "network.follow.created": {++ "version": "1.0.0",++ "owner": "network",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/network.follow.created.v1.json",++ "title": "NetworkFollowCreatedPayload",++ "description": "network.follow.created v1 (OpenVibe.Network; roadmap WS-E task 4, ADR-030). A person followed something: today a Live channel, whose target id is the channel owner's subject. Follows are the network's, kept by Network and keyed by subjects; products such as Live keep a projection they can rebuild. Written to Network's outbox in the transaction that stores the follow. A consumer applies the latest revision per (follower, target) and ignores an older one. Following again what is already followed changes nothing and sends nothing. Envelope: subject { type: user, id: <follower> }, visibility subject, actor the follower.",++ "type": "object",++ "required": [++ "follower",++ "target_type",++ "target_id",++ "revision"++ ],++ "additionalProperties": false,++ "properties": {++ "follower": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "Who follows."++ },++ "target_type": {++ "enum": [++ "channel"++ ],++ "description": "What kind of thing is followed. channel: a Live channel, named by its owner's subject."++ },++ "target_id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The followed target: for a channel, the channel owner's subject."++ },++ "notify_email": {++ "type": "boolean"++ },++ "notify_push": {++ "type": "boolean"++ },++ "revision": {++ "type": "integer",++ "minimum": 1,++ "maximum": 9007199254740991,++ "description": "Grows with every change of this pair."++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ },++ "network.follow.deleted": {++ "version": "1.0.0",++ "owner": "network",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/network.follow.deleted.v1.json",++ "title": "NetworkFollowDeletedPayload",++ "description": "network.follow.deleted v1 (OpenVibe.Network; roadmap WS-E task 4, ADR-030). A person stopped following something (or the follow went with an account). Written to Network's outbox in the transaction that removes the follow; unfollowing what is not followed sends nothing. A consumer applies the latest revision per (follower, target). Envelope: subject { type: user, id: <follower> }, visibility subject, actor the follower (system:network for an account removal).",++ "type": "object",++ "required": [++ "follower",++ "target_type",++ "target_id",++ "revision"++ ],++ "additionalProperties": false,++ "properties": {++ "follower": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "target_type": {++ "enum": [++ "channel"++ ]++ },++ "target_id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "reason": {++ "enum": [++ "unfollowed",++ "account_removed",++ "target_removed"++ ],++ "description": "Why the follow went (default unfollowed)."++ },++ "revision": {++ "type": "integer",++ "minimum": 1,++ "maximum": 9007199254740991++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ },"network.block.changed": {"version": "1.0.0","owner": "network",@@@ -26189,6 +26293,119 @@]}},++ "network.follow-list-result": {++ "version": "1.0.0",++ "owner": "network",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/network/follow-list-result.v1.json",++ "title": "NetworkFollowListResult",++ "description": "network.follow-list-result@1 (ADR-030): a page of follows on OpenVibe.Network, newest first. GET /api/v1/me/follows (what the caller follows), GET /api/v1/follows/:type/:target/followers (who follows a target: the target's owner, or a service with network.follows.read). Lists are never public; counts are.",++ "type": "object",++ "required": [++ "items",++ "next_cursor"++ ],++ "additionalProperties": false,++ "properties": {++ "items": {++ "type": "array",++ "maxItems": 500,++ "items": {++ "type": "object",++ "required": [++ "follower",++ "target_type",++ "target_id",++ "created_at"++ ],++ "additionalProperties": false,++ "properties": {++ "follower": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "target_type": {++ "enum": [++ "channel"++ ]++ },++ "target_id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "notify_email": {++ "type": "boolean"++ },++ "notify_push": {++ "type": "boolean"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ },++ "next_cursor": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 200++ }++ }++ }++ },++ "network.follow-status-result": {++ "version": "1.0.0",++ "owner": "network",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/network/follow-status-result.v1.json",++ "title": "NetworkFollowStatusResult",++ "description": "network.follow-status-result@1 (ADR-030): GET /api/v1/follows/:type/:target on OpenVibe.Network: the public follower count of a target, and, for a signed-in caller, whether they follow it and how they are notified. PUT and DELETE /api/v1/me/follows/:type/:target answer the same document after the change (idempotent: repeating either changes nothing).",++ "type": "object",++ "required": [++ "target_type",++ "target_id",++ "followers"++ ],++ "additionalProperties": false,++ "properties": {++ "target_type": {++ "enum": [++ "channel"++ ]++ },++ "target_id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "followers": {++ "type": "integer",++ "minimum": 0++ },++ "following": {++ "type": "boolean",++ "description": "Present for a signed-in caller."++ },++ "notify_email": {++ "type": "boolean"++ },++ "notify_push": {++ "type": "boolean"++ },++ "since": {++ "type": "string",++ "format": "date-time",++ "description": "When the caller started following."++ }++ }++ }++ },"network.operator-alerts-request": {"version": "1.0.0","owner": "network",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Billing API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Blog API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Chat API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Codes API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Community API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Coupons API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Deals API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Events API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Games API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Host API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -96,8 +96,8 @@"service": "network","name": "OpenVibe.Network","origin": "https://openvibe.network",-- "operations": 12,-- "capabilities": 11++ "operations": 13,++ "capabilities": 12},{"service": "news",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Media API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Network API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -16,6 +16,70 @@}],"paths": {++ "/api/v1/follows/{type}/{target}/followers": {++ "get": {++ "operationId": "get_api_v1_follows_type_target_followers",++ "summary": "Read who follows a target (subjects only), as an owning service: go-live and other notifications, and product projections that rebuild from Network (ADR-030).",++ "description": "**network.follows.read** (first-party): Read who follows a target (subjects only), as an owning service: go-live and other notifications, and product projections that rebuild from Network (ADR-030). Counts are public without it. Never grantable to apps.",++ "tags": [++ "network.follows"++ ],++ "x-openvibe-capabilities": [++ "network.follows.read"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "follows-read",++ "security": [++ {++ "openvibeToken": [++ "network.follows.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "type",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "target",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/network.follow-list-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/api/v1/staff/moderators": {"get": {"operationId": "get_api_v1_staff_moderators",@@@ -1206,6 +1270,64 @@}}},++ "network.follow-list-result.v1": {++ "title": "NetworkFollowListResult",++ "description": "network.follow-list-result@1 (ADR-030): a page of follows on OpenVibe.Network, newest first. GET /api/v1/me/follows (what the caller follows), GET /api/v1/follows/:type/:target/followers (who follows a target: the target's owner, or a service with network.follows.read). Lists are never public; counts are.",++ "type": "object",++ "required": [++ "items",++ "next_cursor"++ ],++ "additionalProperties": false,++ "properties": {++ "items": {++ "type": "array",++ "maxItems": 500,++ "items": {++ "type": "object",++ "required": [++ "follower",++ "target_type",++ "target_id",++ "created_at"++ ],++ "additionalProperties": false,++ "properties": {++ "follower": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "target_type": {++ "enum": [++ "channel"++ ]++ },++ "target_id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "notify_email": {++ "type": "boolean"++ },++ "notify_push": {++ "type": "boolean"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ },++ "next_cursor": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 200++ }++ }++ },"network.github-token-result.v1": {"title": "NetworkGithubTokenResult","description": "network.github-token-result@1: the answer of network.integration.github.read on OpenVibe.Network. GET /internal/integrations/github-token (a service token holding the capability; the shared internal key is refused) → { token, source }: the network's read-only GitHub API token and where it is set (GITHUB_TOKEN in the environment, or the value the owner saved in admin). Answered with Cache-Control: no-store; 404 { error: not_configured, detail } when no token is set.",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.News API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenRe.Stream API",-- "version": "0.64.0",-- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Reviews API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Search API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Sources API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tips API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tools API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Trade API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.VIP API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Wiki API",-- "version": "0.64.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.65.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"@@@ -28611,6 +28611,48 @@ export interface NetworkPrincipalGrantChangedPayload {actor_subject?: string | null;}++/** [email protected] (owner: network) */++/**++ * network.follow.created v1 (OpenVibe.Network; roadmap WS-E task 4, ADR-030). A person followed something: today a Live channel, whose target id is the channel owner's subject. Follows are the network's, kept by Network and keyed by subjects; products such as Live keep a projection they can rebuild. Written to Network's outbox in the transaction that stores the follow. A consumer applies the latest revision per (follower, target) and ignores an older one. Following again what is already followed changes nothing and sends nothing. Envelope: subject { type: user, id: <follower> }, visibility subject, actor the follower.++ */++export interface NetworkFollowCreatedPayload {++ /**++ * Who follows.++ */++ follower: string;++ /**++ * What kind of thing is followed. channel: a Live channel, named by its owner's subject.++ */++ target_type: "channel";++ /**++ * The followed target: for a channel, the channel owner's subject.++ */++ target_id: string;++ notify_email?: boolean;++ notify_push?: boolean;++ /**++ * Grows with every change of this pair.++ */++ revision: number;++ at?: string;++}++++/** [email protected] (owner: network) */++/**++ * network.follow.deleted v1 (OpenVibe.Network; roadmap WS-E task 4, ADR-030). A person stopped following something (or the follow went with an account). Written to Network's outbox in the transaction that removes the follow; unfollowing what is not followed sends nothing. A consumer applies the latest revision per (follower, target). Envelope: subject { type: user, id: <follower> }, visibility subject, actor the follower (system:network for an account removal).++ */++export interface NetworkFollowDeletedPayload {++ follower: string;++ target_type: "channel";++ target_id: string;++ /**++ * Why the follow went (default unfollowed).++ */++ reason?: "unfollowed" | "account_removed" | "target_removed";++ revision: number;++ at?: string;++}++/** [email protected] (owner: network) *//*** network.block.changed v1 (OpenVibe.Network; roadmap WS-E task 5). A person blocked or unblocked someone. Blocks are the network's: kept by Network, keyed by subjects, and honoured by every product — Chat (no DM, no mention notification from someone who blocked you), Community (no reply to their threads or comments) and notifications (none from a person the recipient blocked). Written to Network's outbox in the transaction that changes the block. Consumers keep a projection and apply the latest `revision` per (blocker, blocked); a service may also read the current list at GET /internal/blocks (network.blocks.read). Envelope: subject { type: user, id: <blocker> }, visibility internal, actor the blocker.@@@ -29003,6 +29045,45 @@ export type NetworkNotificationPushResult =total: number;};++/** [email protected] (owner: network) */++/**++ * network.follow-list-result@1 (ADR-030): a page of follows on OpenVibe.Network, newest first. GET /api/v1/me/follows (what the caller follows), GET /api/v1/follows/:type/:target/followers (who follows a target: the target's owner, or a service with network.follows.read). Lists are never public; counts are.++ */++export interface NetworkFollowListResult {++ /**++ * @maxItems 500++ */++ items: {++ follower: string;++ target_type: "channel";++ target_id: string;++ notify_email?: boolean;++ notify_push?: boolean;++ created_at: string;++ }[];++ next_cursor: string | null;++}++++/** [email protected] (owner: network) */++/**++ * network.follow-status-result@1 (ADR-030): GET /api/v1/follows/:type/:target on OpenVibe.Network: the public follower count of a target, and, for a signed-in caller, whether they follow it and how they are notified. PUT and DELETE /api/v1/me/follows/:type/:target answer the same document after the change (idempotent: repeating either changes nothing).++ */++export interface NetworkFollowStatusResult {++ target_type: "channel";++ target_id: string;++ followers: number;++ /**++ * Present for a signed-in caller.++ */++ following?: boolean;++ notify_email?: boolean;++ notify_push?: boolean;++ /**++ * When the caller started following.++ */++ since?: string;++}++/** [email protected] (owner: network) *//*** network.operator-alerts-request@1: POST /internal/operator/alerts on OpenVibe.Network (capability network.operator.alert, held by the Host principal). The complete set of alerts firing now at one source: an alert Network has not seen firing opens and pages the operator; one still firing is reminded once a day; one that was firing and is absent from the set is resolved and says so. An empty list resolves everything from that source. Fingerprints are stable per alert (name and labels), never secrets; summaries and descriptions are the alert rule's annotations.nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "id": "network.follows.read",++ "version": "1.0",++ "owner": "network",++ "status": "active",++ "visibility": "first-party",++ "description": "Read who follows a target (subjects only), as an owning service: go-live and other notifications, and product projections that rebuild from Network (ADR-030). Counts are public without it. Never grantable to apps.",++ "permissions": ["follows:read"],++ "resourceConstraints": [],++ "quotaClass": "follows-read",++ "events": ["network.follow.created", "network.follow.deleted"],++ "implementedBy": ["GET /api/v1/follows/:type/:target/followers"],++ "inputSchema": "common.no-body@1",++ "outputSchema": "network.follow-list-result@1"++}@@@ -38,6 +38,7 @@"network.coins.credit","network.coins.debit","network.coins.transfer",++ "network.follows.read","network.integration.github.read","network.modules.read","network.modules.write",@@@ -53,6 +54,8 @@"network.block.changed","network.credential.revoked","network.credential.rotated",++ "network.follow.created",++ "network.follow.deleted","network.grant.changed","network.module.updated","network.notification.created",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.64.0",++ "version": "0.65.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",
@@ -6,6 +6,13 @@ the notes were in the tag and commit messages (`git tag -n1`).
@@ -6,6 +6,13 @@ the notes were in the tag and commit messages (`git tag -n1`).
## Unreleased
## Unreleased
+## 0.65.0 — 2026-09-26
+
+**The follow graph on Network** (roadmap WS-E task 4, ADR-030).
+- **New event types** `network.follow.created` and `network.follow.deleted` (subject visibility for the follower). Their payloads carry `follower`, `target_type` (`channel` today: a Live channel named by its owner's subject), `target_id`, the notify flags, a per-pair `revision`, and for a delete a `reason`. Products keep projections they can rebuild.
+- **New schemas** `network.follow-status-result@1` (the public follower count, plus the caller's own follow when signed in) and `network.follow-list-result@1` (a page of follows, never public).
+- **New capability** `network.follows.read` (first-party, never for apps): who follows a target, for notifications and projections.
+
## 0.64.0 — 2026-09-26
## 0.64.0 — 2026-09-26
**Wiki VIP spaces and pages** (roadmap WS-K task 8).
**Wiki VIP spaces and pages** (roadmap WS-K task 8).
@@ -2406,6 +2406,26 @@
@@ -2406,6 +2406,26 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "network.follow.created",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "events/payloads/network.follow.created.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active",
+ "adr": "ADR-030"
+ },
+ {
+ "id": "network.follow.deleted",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "events/payloads/network.follow.deleted.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active",
+ "adr": "ADR-030"
+ },
{
{
"id": "network.block.changed",
"id": "network.block.changed",
"version": "1.0.0",
"version": "1.0.0",
@@ -2515,6 +2535,26 @@
@@ -2515,6 +2535,26 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "network.follow-list-result",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "network/follow-list-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active",
+ "adr": "ADR-030"
+ },
+ {
+ "id": "network.follow-status-result",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "network/follow-status-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active",
+ "adr": "ADR-030"
+ },
{
{
"id": "network.operator-alerts-request",
"id": "network.operator-alerts-request",
"version": "1.0.0",
"version": "1.0.0",
new file mode 100644
new file mode 100644
@@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/network.follow.created.v1.json",
+ "title": "NetworkFollowCreatedPayload",
+ "description": "network.follow.created v1 (OpenVibe.Network; roadmap WS-E task 4, ADR-030). A person followed something: today a Live channel, whose target id is the channel owner's subject. Follows are the network's, kept by Network and keyed by subjects; products such as Live keep a projection they can rebuild. Written to Network's outbox in the transaction that stores the follow. A consumer applies the latest revision per (follower, target) and ignores an older one. Following again what is already followed changes nothing and sends nothing. Envelope: subject { type: user, id: <follower> }, visibility subject, actor the follower.",
+ "type": "object",
+ "required": ["follower", "target_type", "target_id", "revision"],
+ "additionalProperties": false,
+ "properties": {
+ "follower": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "Who follows." },
+ "target_type": { "enum": ["channel"], "description": "What kind of thing is followed. channel: a Live channel, named by its owner's subject." },
+ "target_id": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The followed target: for a channel, the channel owner's subject." },
+ "notify_email": { "type": "boolean" },
+ "notify_push": { "type": "boolean" },
+ "revision": { "type": "integer", "minimum": 1, "maximum": 9007199254740991, "description": "Grows with every change of this pair." },
+ "at": { "type": "string", "format": "date-time" }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/network.follow.deleted.v1.json",
+ "title": "NetworkFollowDeletedPayload",
+ "description": "network.follow.deleted v1 (OpenVibe.Network; roadmap WS-E task 4, ADR-030). A person stopped following something (or the follow went with an account). Written to Network's outbox in the transaction that removes the follow; unfollowing what is not followed sends nothing. A consumer applies the latest revision per (follower, target). Envelope: subject { type: user, id: <follower> }, visibility subject, actor the follower (system:network for an account removal).",
+ "type": "object",
+ "required": ["follower", "target_type", "target_id", "revision"],
+ "additionalProperties": false,
+ "properties": {
+ "follower": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" },
+ "target_type": { "enum": ["channel"] },
+ "target_id": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" },
+ "reason": { "enum": ["unfollowed", "account_removed", "target_removed"], "description": "Why the follow went (default unfollowed)." },
+ "revision": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 },
+ "at": { "type": "string", "format": "date-time" }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/network/follow-list-result.v1.json",
+ "title": "NetworkFollowListResult",
+ "description": "network.follow-list-result@1 (ADR-030): a page of follows on OpenVibe.Network, newest first. GET /api/v1/me/follows (what the caller follows), GET /api/v1/follows/:type/:target/followers (who follows a target: the target's owner, or a service with network.follows.read). Lists are never public; counts are.",
+ "type": "object",
+ "required": ["items", "next_cursor"],
+ "additionalProperties": false,
+ "properties": {
+ "items": {
+ "type": "array",
+ "maxItems": 500,
+ "items": {
+ "type": "object",
+ "required": ["follower", "target_type", "target_id", "created_at"],
+ "additionalProperties": false,
+ "properties": {
+ "follower": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" },
+ "target_type": { "enum": ["channel"] },
+ "target_id": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" },
+ "notify_email": { "type": "boolean" },
+ "notify_push": { "type": "boolean" },
+ "created_at": { "type": "string", "format": "date-time" }
+ }
+ }
+ },
+ "next_cursor": { "type": ["string", "null"], "maxLength": 200 }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/network/follow-status-result.v1.json",
+ "title": "NetworkFollowStatusResult",
+ "description": "network.follow-status-result@1 (ADR-030): GET /api/v1/follows/:type/:target on OpenVibe.Network: the public follower count of a target, and, for a signed-in caller, whether they follow it and how they are notified. PUT and DELETE /api/v1/me/follows/:type/:target answer the same document after the change (idempotent: repeating either changes nothing).",
+ "type": "object",
+ "required": ["target_type", "target_id", "followers"],
+ "additionalProperties": false,
+ "properties": {
+ "target_type": { "enum": ["channel"] },
+ "target_id": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" },
+ "followers": { "type": "integer", "minimum": 0 },
+ "following": { "type": "boolean", "description": "Present for a signed-in caller." },
+ "notify_email": { "type": "boolean" },
+ "notify_push": { "type": "boolean" },
+ "since": { "type": "string", "format": "date-time", "description": "When the caller started following." }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "items": [ { "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "created_at": "2026-09-20T10:00:00Z", "username": "alice" } ], "next_cursor": null }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "items": [ { "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "notify_email": true, "notify_push": true, "created_at": "2026-09-20T10:00:00Z" } ], "next_cursor": null }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "followers": -1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "followers": 12 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "followers": 12, "following": true, "notify_email": false, "notify_push": true, "since": "2026-09-20T10:00:00Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "follower": "42", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "revision": 1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "streamer", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "revision": 1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "notify_email": true, "notify_push": true, "revision": 1, "at": "2026-09-26T16:00:00Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "revision": 1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "revision": 0 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "follower": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "target_type": "channel", "target_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "reason": "unfollowed", "revision": 2, "at": "2026-09-26T17:00:00Z" }
@@ -25347,6 +25347,110 @@
@@ -25347,6 +25347,110 @@
}
}
}
}
},
},
+ "network.follow.created": {
+ "version": "1.0.0",
+ "owner": "network",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/network.follow.created.v1.json",
+ "title": "NetworkFollowCreatedPayload",
+ "description": "network.follow.created v1 (OpenVibe.Network; roadmap WS-E task 4, ADR-030). A person followed something: today a Live channel, whose target id is the channel owner's subject. Follows are the network's, kept by Network and keyed by subjects; products such as Live keep a projection they can rebuild. Written to Network's outbox in the transaction that stores the follow. A consumer applies the latest revision per (follower, target) and ignores an older one. Following again what is already followed changes nothing and sends nothing. Envelope: subject { type: user, id: <follower> }, visibility subject, actor the follower.",
+ "type": "object",
+ "required": [
+ "follower",
+ "target_type",
+ "target_id",
+ "revision"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "follower": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "Who follows."
+ },
+ "target_type": {
+ "enum": [
+ "channel"
+ ],
+ "description": "What kind of thing is followed. channel: a Live channel, named by its owner's subject."
+ },
+ "target_id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The followed target: for a channel, the channel owner's subject."
+ },
+ "notify_email": {
+ "type": "boolean"
+ },
+ "notify_push": {
+ "type": "boolean"
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 9007199254740991,
+ "description": "Grows with every change of this pair."
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "network.follow.deleted": {
+ "version": "1.0.0",
+ "owner": "network",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/network.follow.deleted.v1.json",
+ "title": "NetworkFollowDeletedPayload",
+ "description": "network.follow.deleted v1 (OpenVibe.Network; roadmap WS-E task 4, ADR-030). A person stopped following something (or the follow went with an account). Written to Network's outbox in the transaction that removes the follow; unfollowing what is not followed sends nothing. A consumer applies the latest revision per (follower, target). Envelope: subject { type: user, id: <follower> }, visibility subject, actor the follower (system:network for an account removal).",
+ "type": "object",
+ "required": [
+ "follower",
+ "target_type",
+ "target_id",
+ "revision"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "follower": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "target_type": {
+ "enum": [
+ "channel"
+ ]
+ },
+ "target_id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "reason": {
+ "enum": [
+ "unfollowed",
+ "account_removed",
+ "target_removed"
+ ],
+ "description": "Why the follow went (default unfollowed)."
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 9007199254740991
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ },
"network.block.changed": {
"network.block.changed": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
@@ -26189,6 +26293,119 @@
@@ -26189,6 +26293,119 @@
]
]
}
}
},
},
+ "network.follow-list-result": {
+ "version": "1.0.0",
+ "owner": "network",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/network/follow-list-result.v1.json",
+ "title": "NetworkFollowListResult",
+ "description": "network.follow-list-result@1 (ADR-030): a page of follows on OpenVibe.Network, newest first. GET /api/v1/me/follows (what the caller follows), GET /api/v1/follows/:type/:target/followers (who follows a target: the target's owner, or a service with network.follows.read). Lists are never public; counts are.",
+ "type": "object",
+ "required": [
+ "items",
+ "next_cursor"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "items": {
+ "type": "array",
+ "maxItems": 500,
+ "items": {
+ "type": "object",
+ "required": [
+ "follower",
+ "target_type",
+ "target_id",
+ "created_at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "follower": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "target_type": {
+ "enum": [
+ "channel"
+ ]
+ },
+ "target_id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "notify_email": {
+ "type": "boolean"
+ },
+ "notify_push": {
+ "type": "boolean"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "next_cursor": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 200
+ }
+ }
+ }
+ },
+ "network.follow-status-result": {
+ "version": "1.0.0",
+ "owner": "network",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/network/follow-status-result.v1.json",
+ "title": "NetworkFollowStatusResult",
+ "description": "network.follow-status-result@1 (ADR-030): GET /api/v1/follows/:type/:target on OpenVibe.Network: the public follower count of a target, and, for a signed-in caller, whether they follow it and how they are notified. PUT and DELETE /api/v1/me/follows/:type/:target answer the same document after the change (idempotent: repeating either changes nothing).",
+ "type": "object",
+ "required": [
+ "target_type",
+ "target_id",
+ "followers"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "target_type": {
+ "enum": [
+ "channel"
+ ]
+ },
+ "target_id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "followers": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "following": {
+ "type": "boolean",
+ "description": "Present for a signed-in caller."
+ },
+ "notify_email": {
+ "type": "boolean"
+ },
+ "notify_push": {
+ "type": "boolean"
+ },
+ "since": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the caller started following."
+ }
+ }
+ }
+ },
"network.operator-alerts-request": {
"network.operator-alerts-request": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.AI API",
"title": "OpenVibe.AI API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Billing API",
"title": "OpenVibe.Billing API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Blog API",
"title": "OpenVibe.Blog API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Chat API",
"title": "OpenVibe.Chat API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Codes API",
"title": "OpenVibe.Codes API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Community API",
"title": "OpenVibe.Community API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Coupons API",
"title": "OpenVibe.Coupons API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Deals API",
"title": "OpenVibe.Deals API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Events API",
"title": "OpenVibe.Events API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Games API",
"title": "OpenVibe.Games API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Host API",
"title": "OpenVibe.Host API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -96,8 +96,8 @@
@@ -96,8 +96,8 @@
"service": "network",
"service": "network",
"name": "OpenVibe.Network",
"name": "OpenVibe.Network",
"origin": "https://openvibe.network",
"origin": "https://openvibe.network",
- "operations": 12,
- "capabilities": 11
+ "operations": 13,
+ "capabilities": 12
},
},
{
{
"service": "news",
"service": "news",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Live API",
"title": "OpenVibe.Live API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Media API",
"title": "OpenVibe.Media API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Network API",
"title": "OpenVibe.Network API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -16,6 +16,70 @@
@@ -16,6 +16,70 @@
}
}
],
],
"paths": {
"paths": {
+ "/api/v1/follows/{type}/{target}/followers": {
+ "get": {
+ "operationId": "get_api_v1_follows_type_target_followers",
+ "summary": "Read who follows a target (subjects only), as an owning service: go-live and other notifications, and product projections that rebuild from Network (ADR-030).",
+ "description": "**network.follows.read** (first-party): Read who follows a target (subjects only), as an owning service: go-live and other notifications, and product projections that rebuild from Network (ADR-030). Counts are public without it. Never grantable to apps.",
+ "tags": [
+ "network.follows"
+ ],
+ "x-openvibe-capabilities": [
+ "network.follows.read"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "follows-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "network.follows.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "type",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "target",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/network.follow-list-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/v1/staff/moderators": {
"/api/v1/staff/moderators": {
"get": {
"get": {
"operationId": "get_api_v1_staff_moderators",
"operationId": "get_api_v1_staff_moderators",
@@ -1206,6 +1270,64 @@
@@ -1206,6 +1270,64 @@
}
}
}
}
},
},
+ "network.follow-list-result.v1": {
+ "title": "NetworkFollowListResult",
+ "description": "network.follow-list-result@1 (ADR-030): a page of follows on OpenVibe.Network, newest first. GET /api/v1/me/follows (what the caller follows), GET /api/v1/follows/:type/:target/followers (who follows a target: the target's owner, or a service with network.follows.read). Lists are never public; counts are.",
+ "type": "object",
+ "required": [
+ "items",
+ "next_cursor"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "items": {
+ "type": "array",
+ "maxItems": 500,
+ "items": {
+ "type": "object",
+ "required": [
+ "follower",
+ "target_type",
+ "target_id",
+ "created_at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "follower": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "target_type": {
+ "enum": [
+ "channel"
+ ]
+ },
+ "target_id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "notify_email": {
+ "type": "boolean"
+ },
+ "notify_push": {
+ "type": "boolean"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "next_cursor": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 200
+ }
+ }
+ },
"network.github-token-result.v1": {
"network.github-token-result.v1": {
"title": "NetworkGithubTokenResult",
"title": "NetworkGithubTokenResult",
"description": "network.github-token-result@1: the answer of network.integration.github.read on OpenVibe.Network. GET /internal/integrations/github-token (a service token holding the capability; the shared internal key is refused) → { token, source }: the network's read-only GitHub API token and where it is set (GITHUB_TOKEN in the environment, or the value the owner saved in admin). Answered with Cache-Control: no-store; 404 { error: not_configured, detail } when no token is set.",
"description": "network.github-token-result@1: the answer of network.integration.github.read on OpenVibe.Network. GET /internal/integrations/github-token (a service token holding the capability; the shared internal key is refused) → { token, source }: the network's read-only GitHub API token and where it is set (GITHUB_TOKEN in the environment, or the value the owner saved in admin). Answered with Cache-Control: no-store; 404 { error: not_configured, detail } when no token is set.",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.News API",
"title": "OpenVibe.News API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenRe.Stream API",
"title": "OpenRe.Stream API",
- "version": "0.64.0",
- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Reviews API",
"title": "OpenVibe.Reviews API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Search API",
"title": "OpenVibe.Search API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Sources API",
"title": "OpenVibe.Sources API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tips API",
"title": "OpenVibe.Tips API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tools API",
"title": "OpenVibe.Tools API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Trade API",
"title": "OpenVibe.Trade API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.VIP API",
"title": "OpenVibe.VIP API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Wiki API",
"title": "OpenVibe.Wiki API",
- "version": "0.64.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.64.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.65.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.65.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": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -28611,6 +28611,48 @@ export interface NetworkPrincipalGrantChangedPayload {
@@ -28611,6 +28611,48 @@ export interface NetworkPrincipalGrantChangedPayload {
actor_subject?: string | null;
actor_subject?: string | null;
}
}
+/** [email protected] (owner: network) */
+/**
+ * network.follow.created v1 (OpenVibe.Network; roadmap WS-E task 4, ADR-030). A person followed something: today a Live channel, whose target id is the channel owner's subject. Follows are the network's, kept by Network and keyed by subjects; products such as Live keep a projection they can rebuild. Written to Network's outbox in the transaction that stores the follow. A consumer applies the latest revision per (follower, target) and ignores an older one. Following again what is already followed changes nothing and sends nothing. Envelope: subject { type: user, id: <follower> }, visibility subject, actor the follower.
+ */
+export interface NetworkFollowCreatedPayload {
+ /**
+ * Who follows.
+ */
+ follower: string;
+ /**
+ * What kind of thing is followed. channel: a Live channel, named by its owner's subject.
+ */
+ target_type: "channel";
+ /**
+ * The followed target: for a channel, the channel owner's subject.
+ */
+ target_id: string;
+ notify_email?: boolean;
+ notify_push?: boolean;
+ /**
+ * Grows with every change of this pair.
+ */
+ revision: number;
+ at?: string;
+}
+
+/** [email protected] (owner: network) */
+/**
+ * network.follow.deleted v1 (OpenVibe.Network; roadmap WS-E task 4, ADR-030). A person stopped following something (or the follow went with an account). Written to Network's outbox in the transaction that removes the follow; unfollowing what is not followed sends nothing. A consumer applies the latest revision per (follower, target). Envelope: subject { type: user, id: <follower> }, visibility subject, actor the follower (system:network for an account removal).
+ */
+export interface NetworkFollowDeletedPayload {
+ follower: string;
+ target_type: "channel";
+ target_id: string;
+ /**
+ * Why the follow went (default unfollowed).
+ */
+ reason?: "unfollowed" | "account_removed" | "target_removed";
+ revision: number;
+ at?: string;
+}
+
/** [email protected] (owner: network) */
/** [email protected] (owner: network) */
/**
/**
* network.block.changed v1 (OpenVibe.Network; roadmap WS-E task 5). A person blocked or unblocked someone. Blocks are the network's: kept by Network, keyed by subjects, and honoured by every product — Chat (no DM, no mention notification from someone who blocked you), Community (no reply to their threads or comments) and notifications (none from a person the recipient blocked). Written to Network's outbox in the transaction that changes the block. Consumers keep a projection and apply the latest `revision` per (blocker, blocked); a service may also read the current list at GET /internal/blocks (network.blocks.read). Envelope: subject { type: user, id: <blocker> }, visibility internal, actor the blocker.
* network.block.changed v1 (OpenVibe.Network; roadmap WS-E task 5). A person blocked or unblocked someone. Blocks are the network's: kept by Network, keyed by subjects, and honoured by every product — Chat (no DM, no mention notification from someone who blocked you), Community (no reply to their threads or comments) and notifications (none from a person the recipient blocked). Written to Network's outbox in the transaction that changes the block. Consumers keep a projection and apply the latest `revision` per (blocker, blocked); a service may also read the current list at GET /internal/blocks (network.blocks.read). Envelope: subject { type: user, id: <blocker> }, visibility internal, actor the blocker.
@@ -29003,6 +29045,45 @@ export type NetworkNotificationPushResult =
@@ -29003,6 +29045,45 @@ export type NetworkNotificationPushResult =
total: number;
total: number;
};
};
+/** [email protected] (owner: network) */
+/**
+ * network.follow-list-result@1 (ADR-030): a page of follows on OpenVibe.Network, newest first. GET /api/v1/me/follows (what the caller follows), GET /api/v1/follows/:type/:target/followers (who follows a target: the target's owner, or a service with network.follows.read). Lists are never public; counts are.
+ */
+export interface NetworkFollowListResult {
+ /**
+ * @maxItems 500
+ */
+ items: {
+ follower: string;
+ target_type: "channel";
+ target_id: string;
+ notify_email?: boolean;
+ notify_push?: boolean;
+ created_at: string;
+ }[];
+ next_cursor: string | null;
+}
+
+/** [email protected] (owner: network) */
+/**
+ * network.follow-status-result@1 (ADR-030): GET /api/v1/follows/:type/:target on OpenVibe.Network: the public follower count of a target, and, for a signed-in caller, whether they follow it and how they are notified. PUT and DELETE /api/v1/me/follows/:type/:target answer the same document after the change (idempotent: repeating either changes nothing).
+ */
+export interface NetworkFollowStatusResult {
+ target_type: "channel";
+ target_id: string;
+ followers: number;
+ /**
+ * Present for a signed-in caller.
+ */
+ following?: boolean;
+ notify_email?: boolean;
+ notify_push?: boolean;
+ /**
+ * When the caller started following.
+ */
+ since?: string;
+}
+
/** [email protected] (owner: network) */
/** [email protected] (owner: network) */
/**
/**
* network.operator-alerts-request@1: POST /internal/operator/alerts on OpenVibe.Network (capability network.operator.alert, held by the Host principal). The complete set of alerts firing now at one source: an alert Network has not seen firing opens and pages the operator; one still firing is reminded once a day; one that was firing and is absent from the set is resolved and says so. An empty list resolves everything from that source. Fingerprints are stable per alert (name and labels), never secrets; summaries and descriptions are the alert rule's annotations.
* network.operator-alerts-request@1: POST /internal/operator/alerts on OpenVibe.Network (capability network.operator.alert, held by the Host principal). The complete set of alerts firing now at one source: an alert Network has not seen firing opens and pages the operator; one still firing is reminded once a day; one that was firing and is absent from the set is resolved and says so. An empty list resolves everything from that source. Fingerprints are stable per alert (name and labels), never secrets; summaries and descriptions are the alert rule's annotations.
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "id": "network.follows.read",
+ "version": "1.0",
+ "owner": "network",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Read who follows a target (subjects only), as an owning service: go-live and other notifications, and product projections that rebuild from Network (ADR-030). Counts are public without it. Never grantable to apps.",
+ "permissions": ["follows:read"],
+ "resourceConstraints": [],
+ "quotaClass": "follows-read",
+ "events": ["network.follow.created", "network.follow.deleted"],
+ "implementedBy": ["GET /api/v1/follows/:type/:target/followers"],
+ "inputSchema": "common.no-body@1",
+ "outputSchema": "network.follow-list-result@1"
+}
@@ -38,6 +38,7 @@
@@ -38,6 +38,7 @@
"network.coins.credit",
"network.coins.credit",
"network.coins.debit",
"network.coins.debit",
"network.coins.transfer",
"network.coins.transfer",
+ "network.follows.read",
"network.integration.github.read",
"network.integration.github.read",
"network.modules.read",
"network.modules.read",
"network.modules.write",
"network.modules.write",
@@ -53,6 +54,8 @@
@@ -53,6 +54,8 @@
"network.block.changed",
"network.block.changed",
"network.credential.revoked",
"network.credential.revoked",
"network.credential.rotated",
"network.credential.rotated",
+ "network.follow.created",
+ "network.follow.deleted",
"network.grant.changed",
"network.grant.changed",
"network.module.updated",
"network.module.updated",
"network.notification.created",
"network.notification.created",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.64.0",
+ "version": "0.65.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
"types": "generated/typescript/index.d.ts",