openvibers/openvibe.contracts Commits
v0.49.0: platform blocks (WS-E task 5) — network.block.changed (blocker, blocked, active, per-pair revision; consumed by Chat and Community) and the network.blocks.read capability (GET /internal/blocks)
Changes
@@@ -4,6 +4,10 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) thfrom `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,the notes were in the tag and commit messages (`git tag -n1`).++## 0.49.0 — 2026-09-25++++Platform blocks (roadmap WS-E task 5). **`network.block.changed`**: a person blocked or unblocked someone (`blocker`, `blocked`, `active`, a per-pair `revision`); Network keeps the blocks, keyed by subjects, and Chat and Community consume the event. New capability **`network.blocks.read`** (first-party, never for apps): `GET /internal/blocks?subject=` lists who a person blocked and who blocked them. Fixtures for a block and an unblock. Additive.++## 0.48.0 — 2026-09-25**Correction to 0.47.0.** `network.grant.changed` was already emitted by Network's developer event relay for a developer app's grant, and OpenVibe.Events acts on it (it stops an app's subscriptions when its `events.app.subscribe` grant leaves approved). 0.47.0 gave that event a service-principal payload that does not match. Now `network.grant.changed` describes what is actually sent: `{ project_id, capability, audience, from, to }`, subject `app`. A first-party service principal's grant change is the new **`network.principal_grant.changed`** (the 0.47.0 payload under its own name). Nothing consumed the 0.47.0 shape. The compat gate records this in `compatibility/corrections.json` (accepted against v0.47.0 only).@@@ -945,6 +945,15 @@"compatibility": "backward","status": "active"},++ {++ "id": "network.block.changed",++ "version": "1.0.0",++ "owner": "network",++ "schema": "events/payloads/network.block.changed.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "network.user.updated","version": "1.0.0",nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/network.block.changed.v1.json",++ "title": "NetworkBlockChangedPayload",++ "description": "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.",++ "type": "object",++ "required": ["blocker", "blocked", "active", "revision"],++ "additionalProperties": false,++ "properties": {++ "blocker": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "Who blocked." },++ "blocked": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "Who is blocked." },++ "active": { "type": "boolean", "description": "true: blocked; false: unblocked." },++ "revision": { "type": "integer", "minimum": 1, "maximum": 9007199254740991, "description": "Grows with every change of this pair; a consumer ignores an older one." },++ "at": { "type": "string", "format": "date-time" }++ }++}nnew file mode 100644@@@ -0,0 +1 @@++{ "blocker": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "blocked": "bob", "active": true, "revision": 1 }nnew file mode 100644@@@ -0,0 +1 @@++{ "blocker": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "blocked": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "active": true, "revision": 1, "at": "2026-09-25T22:00:00.000Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "blocker": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "blocked": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "active": false, "revision": 2 }@@@ -10456,6 +10456,51 @@}}},++ "network.block.changed": {++ "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.block.changed.v1.json",++ "title": "NetworkBlockChangedPayload",++ "description": "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.",++ "type": "object",++ "required": [++ "blocker",++ "blocked",++ "active",++ "revision"++ ],++ "additionalProperties": false,++ "properties": {++ "blocker": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "Who blocked."++ },++ "blocked": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "Who is blocked."++ },++ "active": {++ "type": "boolean",++ "description": "true: blocked; false: unblocked."++ },++ "revision": {++ "type": "integer",++ "minimum": 1,++ "maximum": 9007199254740991,++ "description": "Grows with every change of this pair; a consumer ignores an older one."++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ },"network.user.updated": {"version": "1.0.0","owner": "network",@@@ -9503,6 +9503,30 @@ export interface NetworkPrincipalGrantChangedPayload {actor_subject?: string | null;}++/** [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.++ */++export interface NetworkBlockChangedPayload {++ /**++ * Who blocked.++ */++ blocker: string;++ /**++ * Who is blocked.++ */++ blocked: string;++ /**++ * true: blocked; false: unblocked.++ */++ active: boolean;++ /**++ * Grows with every change of this pair; a consumer ignores an older one.++ */++ revision: number;++ at?: string;++}++/** [email protected] (owner: network) *//*** network.user.updated v1 (OpenVibe.Network server/identity/profile-events.js; roadmap WS-B task 2). What other services may know about a person changed: their username, display name, picture, colour, role or ban. The payload is the person's whole current profile, not a diff, so a consumer keeps a projection by writing it over the one it has, and ignores one whose revision is not newer (events can arrive out of order). Emitted in the transaction of the change: a profile edit, a rename, a role grant or removal, a ban or unban. Consumers use it instead of their own copies: roles go both ways (a downgrade too), and a ban arrives even for someone who never comes back. Envelope: subject { type: user, id }, visibility internal, actor the person (their own edit) or the staff member.nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "id": "network.blocks.read",++ "version": "1.0",++ "owner": "network",++ "status": "active",++ "visibility": "first-party",++ "description": "Read who a person has blocked and who has blocked them (subjects only), as an owning service honouring platform blocks: Chat for DMs and mentions, Community for replies, notification senders. Never grantable to apps.",++ "permissions": ["blocks:read"],++ "resourceConstraints": [],++ "quotaClass": "blocks-read",++ "events": ["network.block.changed"],++ "implementedBy": ["GET /internal/blocks?subject=usr_…"]++}@@@ -23,6 +23,7 @@"chat.moderation.action"],"eventsConsumed": [++ "network.block.changed","network.user.token_valid_after"],"namespacesOwned": [@@@ -38,6 +38,7 @@"community.thread.created"],"eventsConsumed": [++ "network.block.changed","network.user.token_valid_after"],"namespacesOwned": [@@@ -34,6 +34,7 @@"capabilities": ["identity.subject.resolve","mods.grant.manage",++ "network.blocks.read","network.coins.credit","network.coins.debit","network.coins.transfer",@@@ -48,6 +49,7 @@"eventsProduced": ["network.app.created","network.app.revoked",++ "network.block.changed","network.credential.revoked","network.credential.rotated","network.grant.changed",@@@ -1,12 +1,12 @@{"name": "openvibe-contracts",-- "version": "0.48.0",++ "version": "0.49.0","lockfileVersion": 3,"requires": true,"packages": {"": {"name": "openvibe-contracts",-- "version": "0.48.0",++ "version": "0.49.0","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.48.0",++ "version": "0.49.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",
@@ -4,6 +4,10 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
@@ -4,6 +4,10 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
the notes were in the tag and commit messages (`git tag -n1`).
+## 0.49.0 — 2026-09-25
+
+Platform blocks (roadmap WS-E task 5). **`network.block.changed`**: a person blocked or unblocked someone (`blocker`, `blocked`, `active`, a per-pair `revision`); Network keeps the blocks, keyed by subjects, and Chat and Community consume the event. New capability **`network.blocks.read`** (first-party, never for apps): `GET /internal/blocks?subject=` lists who a person blocked and who blocked them. Fixtures for a block and an unblock. Additive.
+
## 0.48.0 — 2026-09-25
## 0.48.0 — 2026-09-25
**Correction to 0.47.0.** `network.grant.changed` was already emitted by Network's developer event relay for a developer app's grant, and OpenVibe.Events acts on it (it stops an app's subscriptions when its `events.app.subscribe` grant leaves approved). 0.47.0 gave that event a service-principal payload that does not match. Now `network.grant.changed` describes what is actually sent: `{ project_id, capability, audience, from, to }`, subject `app`. A first-party service principal's grant change is the new **`network.principal_grant.changed`** (the 0.47.0 payload under its own name). Nothing consumed the 0.47.0 shape. The compat gate records this in `compatibility/corrections.json` (accepted against v0.47.0 only).
**Correction to 0.47.0.** `network.grant.changed` was already emitted by Network's developer event relay for a developer app's grant, and OpenVibe.Events acts on it (it stops an app's subscriptions when its `events.app.subscribe` grant leaves approved). 0.47.0 gave that event a service-principal payload that does not match. Now `network.grant.changed` describes what is actually sent: `{ project_id, capability, audience, from, to }`, subject `app`. A first-party service principal's grant change is the new **`network.principal_grant.changed`** (the 0.47.0 payload under its own name). Nothing consumed the 0.47.0 shape. The compat gate records this in `compatibility/corrections.json` (accepted against v0.47.0 only).
@@ -945,6 +945,15 @@
@@ -945,6 +945,15 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "network.block.changed",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "events/payloads/network.block.changed.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "network.user.updated",
"id": "network.user.updated",
"version": "1.0.0",
"version": "1.0.0",
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/network.block.changed.v1.json",
+ "title": "NetworkBlockChangedPayload",
+ "description": "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.",
+ "type": "object",
+ "required": ["blocker", "blocked", "active", "revision"],
+ "additionalProperties": false,
+ "properties": {
+ "blocker": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "Who blocked." },
+ "blocked": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "Who is blocked." },
+ "active": { "type": "boolean", "description": "true: blocked; false: unblocked." },
+ "revision": { "type": "integer", "minimum": 1, "maximum": 9007199254740991, "description": "Grows with every change of this pair; a consumer ignores an older one." },
+ "at": { "type": "string", "format": "date-time" }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "blocker": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "blocked": "bob", "active": true, "revision": 1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "blocker": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "blocked": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "active": true, "revision": 1, "at": "2026-09-25T22:00:00.000Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "blocker": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "blocked": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "active": false, "revision": 2 }
@@ -10456,6 +10456,51 @@
@@ -10456,6 +10456,51 @@
}
}
}
}
},
},
+ "network.block.changed": {
+ "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.block.changed.v1.json",
+ "title": "NetworkBlockChangedPayload",
+ "description": "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.",
+ "type": "object",
+ "required": [
+ "blocker",
+ "blocked",
+ "active",
+ "revision"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "blocker": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "Who blocked."
+ },
+ "blocked": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "Who is blocked."
+ },
+ "active": {
+ "type": "boolean",
+ "description": "true: blocked; false: unblocked."
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 9007199254740991,
+ "description": "Grows with every change of this pair; a consumer ignores an older one."
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ },
"network.user.updated": {
"network.user.updated": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
@@ -9503,6 +9503,30 @@ export interface NetworkPrincipalGrantChangedPayload {
@@ -9503,6 +9503,30 @@ export interface NetworkPrincipalGrantChangedPayload {
actor_subject?: string | null;
actor_subject?: string | null;
}
}
+/** [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.
+ */
+export interface NetworkBlockChangedPayload {
+ /**
+ * Who blocked.
+ */
+ blocker: string;
+ /**
+ * Who is blocked.
+ */
+ blocked: string;
+ /**
+ * true: blocked; false: unblocked.
+ */
+ active: boolean;
+ /**
+ * Grows with every change of this pair; a consumer ignores an older one.
+ */
+ revision: number;
+ at?: string;
+}
+
/** [email protected] (owner: network) */
/** [email protected] (owner: network) */
/**
/**
* network.user.updated v1 (OpenVibe.Network server/identity/profile-events.js; roadmap WS-B task 2). What other services may know about a person changed: their username, display name, picture, colour, role or ban. The payload is the person's whole current profile, not a diff, so a consumer keeps a projection by writing it over the one it has, and ignores one whose revision is not newer (events can arrive out of order). Emitted in the transaction of the change: a profile edit, a rename, a role grant or removal, a ban or unban. Consumers use it instead of their own copies: roles go both ways (a downgrade too), and a ban arrives even for someone who never comes back. Envelope: subject { type: user, id }, visibility internal, actor the person (their own edit) or the staff member.
* network.user.updated v1 (OpenVibe.Network server/identity/profile-events.js; roadmap WS-B task 2). What other services may know about a person changed: their username, display name, picture, colour, role or ban. The payload is the person's whole current profile, not a diff, so a consumer keeps a projection by writing it over the one it has, and ignores one whose revision is not newer (events can arrive out of order). Emitted in the transaction of the change: a profile edit, a rename, a role grant or removal, a ban or unban. Consumers use it instead of their own copies: roles go both ways (a downgrade too), and a ban arrives even for someone who never comes back. Envelope: subject { type: user, id }, visibility internal, actor the person (their own edit) or the staff member.
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "id": "network.blocks.read",
+ "version": "1.0",
+ "owner": "network",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Read who a person has blocked and who has blocked them (subjects only), as an owning service honouring platform blocks: Chat for DMs and mentions, Community for replies, notification senders. Never grantable to apps.",
+ "permissions": ["blocks:read"],
+ "resourceConstraints": [],
+ "quotaClass": "blocks-read",
+ "events": ["network.block.changed"],
+ "implementedBy": ["GET /internal/blocks?subject=usr_…"]
+}
@@ -23,6 +23,7 @@
@@ -23,6 +23,7 @@
"chat.moderation.action"
"chat.moderation.action"
],
],
"eventsConsumed": [
"eventsConsumed": [
+ "network.block.changed",
"network.user.token_valid_after"
"network.user.token_valid_after"
],
],
"namespacesOwned": [
"namespacesOwned": [
@@ -38,6 +38,7 @@
@@ -38,6 +38,7 @@
"community.thread.created"
"community.thread.created"
],
],
"eventsConsumed": [
"eventsConsumed": [
+ "network.block.changed",
"network.user.token_valid_after"
"network.user.token_valid_after"
],
],
"namespacesOwned": [
"namespacesOwned": [
@@ -34,6 +34,7 @@
@@ -34,6 +34,7 @@
"capabilities": [
"capabilities": [
"identity.subject.resolve",
"identity.subject.resolve",
"mods.grant.manage",
"mods.grant.manage",
+ "network.blocks.read",
"network.coins.credit",
"network.coins.credit",
"network.coins.debit",
"network.coins.debit",
"network.coins.transfer",
"network.coins.transfer",
@@ -48,6 +49,7 @@
@@ -48,6 +49,7 @@
"eventsProduced": [
"eventsProduced": [
"network.app.created",
"network.app.created",
"network.app.revoked",
"network.app.revoked",
+ "network.block.changed",
"network.credential.revoked",
"network.credential.revoked",
"network.credential.rotated",
"network.credential.rotated",
"network.grant.changed",
"network.grant.changed",
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.48.0",
+ "version": "0.49.0",
"lockfileVersion": 3,
"lockfileVersion": 3,
"requires": true,
"requires": true,
"packages": {
"packages": {
"": {
"": {
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.48.0",
+ "version": "0.49.0",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.48.0",
+ "version": "0.49.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",