openvibers/openvibe.contracts Commits
Apps ask for consent per purpose at sign-in: the consent claim (0.310.0) (#318)
Changes
@@@ -1,5 +1,14 @@# Changelog++## 0.310.0 — 2026-11-07++++- Apps ask for consent per purpose at sign-in (the consent plan's C4). An app adds one scope `consent:<purpose>` per purpose to /oauth/authorize, beside its other scopes. It may ask for every purpose of the consent family except the retired `ai.looks`: `ai.train`, `data.public`, `face.recognize` and `app.data`. Any other `consent:` scope is refused with `invalid_scope`. The consent screen lists each purpose in plain words, nothing ticked, and the person can sign in with every purpose refused. A refused purpose is not asked again by that app for 30 days, unless the person asks for it again on their account page.++- Add `network.consent-claim@1`: the `consent` claim of the tokens Network issues from that sign-in (a first-party user token, a Dock token or an app token). It holds exactly the purposes the sign-in asked for that the person granted to that app and that are active when the token is issued, `[]` when none. It never holds a refused purpose or another app's grant, and the app learns only "not granted". The claim is absent when no consent scope was asked. Every new token works it out again, so a purpose turned off is gone from the next one.++- `identity.service-token-claims@1` gains the optional `consent` claim (additive): an app token from the authorization-code flow carries it when the sign-in asked for consent scopes.++- Add `network.me-consent-apps-result@1`: "Apps you allowed" on the account page (`GET /api/v1/me/consent/apps`), listing each app's granted purposes and the ones refused in the last 30 days (`quiet`, with the day it ends), with the words the app consent screen shows. `network.consent.manage` names that route and `DELETE /api/v1/me/consent/:purpose/:client/refusal`, which ends a refusal's quiet days so the app's next sign-in asks again.++- `network.consent@1`: an app's grant is one record per purpose, with the app as `client` and `source` `oauth`. `client` is no longer only for `app.data`, and `options.purposes` is not written any more (kept so older records validate). `network.consent-check-request@1`'s `client` may name the app for any purpose: one app's grant never answers for OpenVibe itself or for another app. Descriptions only: no shape changes.++- Valid and invalid fixtures cover a user token with one purpose, an app token with none and with all four, a retired, unknown, duplicated or missing claim, a claim that is not a list, an app's grant record, the account page with two apps and with none, a bad client id, a retired purpose, an extra key and a quiet refusal with no end.++## 0.309.0 — 2026-11-07- Add `bot.machine-pool@1` (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC (`rpc-server` on the members, `llama-server --rpc` on the head). OpenVibe.Network holds the record: the owner proposes it on /machines with two to eight of their own machines (the default head has the most free VRAM), and each machine reads it with its own node token and approves it on itself. A member's `certificate` is null until it approves; then it is that machine's self-signed Ed25519 public certificate, which the other members pin for the pool's private link. The private key stays on the machine: a certificate slot holding anything with `PRIVATE KEY` in it is refused, and so is any field the schema does not name. A member's `address` is a private RFC 1918 address and prefix (never 0.0.0.0, loopback or a public address) and its `public_ip` is the address Network saw it from. The rules a schema cannot say are written in the description and checked by Network and Node: the head is a member, every member is the owner's today, all share one public address and one private subnet, the head's direct link probe to each passes, every key is its own, and any change of membership, owner, head or key revokes the pool so every machine approves a new one.@@@ -8323,6 +8323,24 @@"compatibility": "backward","status": "active"},++ {++ "id": "network.consent-claim",++ "version": "1.0.0",++ "owner": "network",++ "schema": "network/consent-claim.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "network.me-consent-apps-result",++ "version": "1.0.0",++ "owner": "network",++ "schema": "network/me-consent-apps-result.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },{"id": "network.consent-check-request","version": "1.0.0",@@@ -92,6 +92,20 @@"pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$","description": "The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Also present on a node token that asks for an audience where the machine acts for its owner (Agent Streams S1: openvibe.live and live.agent_stream.publish); absent on every other client_credentials token."},++ "consent": {++ "type": "array",++ "maxItems": 4,++ "uniqueItems": true,++ "items": {++ "enum": [++ "ai.train",++ "data.public",++ "face.recognize",++ "app.data"++ ]++ },++ "description": "App tokens from the authorization-code flow (0.310.0): the purposes the person (on_behalf_of) granted this app at sign-in and that are still active when the token is issued, the claim network.consent-claim@1 defines. Present when the sign-in asked for consent:<purpose> scopes, [] when none is granted, absent otherwise."++ },"act": {"type": "object","required": [@@@ -18,7 +18,7 @@"client": {"type": "string","pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",-- "description": "app.data only: the app asking. Required for app.data."++ "description": "The app the use is for. Required for app.data. For any other purpose it asks about that app's own grant from its sign-in (consent:<purpose>, 0.310.0); without it the answer is about OpenVibe's own use. One app's grant never answers for OpenVibe or for another app."},"feature": {"enum": ["verify", "find", "cast", "library"],@@@ -2,7 +2,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/network/consent-check-result.v1.json","title": "NetworkConsentCheckResult",-- "description": "network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.",++ "description": "network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An app's grant (client set) answers only a check that names that app. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.","type": "object","additionalProperties": false,"required": ["purpose", "allowed", "checked_at"],nnew file mode 100644@@@ -0,0 +1,18 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/network/consent-claim.v1.json",++ "title": "NetworkConsentClaim",++ "description": "network.consent-claim@1 (the consent plan's C4, 2026-11-07): the purposes a person granted to one app at sign-in, as the `consent` claim of the tokens OpenVibe.Network issues to that app. An app asks with one scope `consent:<purpose>` per purpose, beside its other scopes, in /oauth/authorize. The purposes an app may ask for are network.consent@1's except the retired ai.looks: ai.train, data.public, face.recognize and app.data. Any other `consent:` scope is refused with invalid_scope. The consent screen lists each purpose in plain words, off until the person ticks it, and the person can sign in with every purpose refused. A granted purpose is a network.consent@1 record with the app's OAuth client id as client and source oauth; the person turns it off on their account page (DELETE /api/v1/me/consent/:purpose/:client). A refused purpose is not asked again by that app for 30 days, unless the person asks for it again on their account page. The schema is the claims object of any token that carries the claim: a first-party user access token, a Dock token, or an app token (identity.service-token-claims@1).",++ "type": "object",++ "required": ["consent"],++ "additionalProperties": true,++ "properties": {++ "consent": {++ "type": "array",++ "maxItems": 4,++ "uniqueItems": true,++ "items": { "enum": ["ai.train", "data.public", "face.recognize", "app.data"] },++ "description": "Exactly the purposes this sign-in asked for that the person has granted to this app and that are active when the token is issued, in network.consent@1's order. Never a refused or unasked purpose and never another app's grant. An empty list means none is granted: the app learns \"not granted\", never whether the person refused or was not asked. The claim is absent when the sign-in asked for no consent scope. Every new token works it out again, so a purpose the person turns off is gone from the next one (a refresh, or the next code); a token already issued keeps it until it expires. A service about to use a person's content for an app still asks POST /internal/consent/check with the app as client. The token response's `scope` lists consent:<purpose> only for the purposes granted."++ }++ }++}@@@ -35,7 +35,7 @@"client": {"type": ["string", "null"],"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",-- "description": "The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of an app the person granted (required for app.data, and only there)."++ "description": "The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of the app the person granted it to. app.data always names one. Since 0.310.0 an app may ask for any purpose at sign-in (consent:<purpose>, network.consent-claim@1): the grant is one record per purpose with this client and source oauth, and it allows that app only, never OpenVibe itself or another app."},"options": {"type": "object",@@@ -57,7 +57,7 @@"maxItems": 4,"uniqueItems": true,"items": { "enum": ["ai.train", "data.public", "face.recognize", "ai.looks"] },-- "description": "app.data only: what the app may do with the person's content, as the consent screen showed it."++ "description": "app.data only, and not written since 0.310.0: an app's consent screen grants each purpose as its own record (client set), so a reader asks for the purpose itself. Kept so older records still validate."}},"description": "What the purpose needs beyond on and kinds. An empty object for ai.train and ai.looks."@@@ -69,7 +69,7 @@},"source": {"enum": ["account", "oauth", "api", "import:vision"],-- "description": "Where the choice was made: the account page, an app's consent screen, the person's own API call, or OpenVibe.Vision's older face consents moved into Network."++ "description": "Where the choice was made: the account page, an app's consent screen at sign-in (consent:<purpose>, with client set), the person's own API call, or OpenVibe.Vision's older face consents moved into Network."},"granted_at": {"type": "string",nnew file mode 100644@@@ -0,0 +1,75 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/network/me-consent-apps-result.v1.json",++ "title": "NetworkMeConsentAppsResult",++ "description": "network.me-consent-apps-result@1 (the consent plan's C4, 2026-11-07): \"Apps you allowed\" on the signed-in person's account page. GET /api/v1/me/consent/apps on OpenVibe.Network. It lists every app the person granted a purpose to on its sign-in screen (consent:<purpose>, network.consent-claim@1), with the purposes granted and those refused in the last 30 days. It also carries the words the app consent screen shows for each purpose. Only the person's own session reads it; an app never sees this list, its own entry included.",++ "type": "object",++ "additionalProperties": false,++ "required": ["purposes", "apps"],++ "properties": {++ "purposes": {++ "type": "array",++ "maxItems": 8,++ "description": "The purposes an app may ask for, with the exact words the app consent screen shows.",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": ["id", "title", "allows", "cannot_undo", "adults_only", "text_version"],++ "properties": {++ "id": { "enum": ["ai.train", "data.public", "face.recognize", "app.data"] },++ "title": { "type": "string", "minLength": 1, "maxLength": 80 },++ "allows": { "type": "string", "minLength": 1, "maxLength": 300, "description": "What ticking it allows the app, in one plain line." },++ "cannot_undo": { "type": "string", "maxLength": 300, "description": "What turning it off later cannot undo (empty when nothing)." },++ "adults_only": { "type": "boolean", "description": "Ticking it asks the person to confirm they are 18 or older." },++ "text_version": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$" }++ }++ }++ },++ "apps": {++ "type": "array",++ "maxItems": 200,++ "description": "One entry per app with a purpose granted or refused, apps with an active grant first, then by name.",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": ["client", "name", "third_party", "granted", "quiet"],++ "properties": {++ "client": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$", "description": "The app's OAuth client id." },++ "name": { "type": "string", "minLength": 1, "maxLength": 120, "description": "The app's name as its developer (or OpenVibe, for its own sites) registered it." },++ "third_party": { "type": "boolean", "description": "True for a developer's app (app_<ULID>), false for an OpenVibe site." },++ "granted": {++ "type": "array",++ "maxItems": 4,++ "description": "The purposes the app holds now (active network.consent@1 records with this client).",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": ["consent_id", "purpose", "granted_at", "text_version"],++ "properties": {++ "consent_id": { "type": "string", "pattern": "^cns_[0-9A-HJKMNP-TV-Z]{26}$" },++ "purpose": { "enum": ["ai.train", "data.public", "face.recognize", "app.data"] },++ "granted_at": { "type": "string", "format": "date-time" },++ "text_version": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$" }++ }++ }++ },++ "quiet": {++ "type": "array",++ "maxItems": 4,++ "description": "The purposes the person refused this app in the last 30 days: the app does not ask for them again until quiet_until. DELETE /api/v1/me/consent/:purpose/:client/refusal ends that early, so the next sign-in asks.",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": ["purpose", "refused_at", "quiet_until"],++ "properties": {++ "purpose": { "enum": ["ai.train", "data.public", "face.recognize", "app.data"] },++ "refused_at": { "type": "string", "format": "date-time" },++ "quiet_until": { "type": "string", "format": "date-time" }++ }++ }++ }++ }++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "iss": "https://openvibe.network",++ "sub": "app:app_01K6R3A1B2C3D4E5F6G7H8J9KD",++ "actor_type": "app",++ "aud": [++ "openvibe.media"++ ],++ "cap": [],++ "ns": [++ "prj_01J0000000000000000000000Z",++ "app.prj_01J0000000000000000000000Z.*"++ ],++ "iat": 1790000000,++ "exp": 1790000300,++ "jti": "tok_abcdef21",++ "project_id": "prj_01J0000000000000000000000Z",++ "env": "production",++ "on_behalf_of": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",++ "consent": [++ "ai.looks"++ ]++}nnew file mode 100644@@@ -0,0 +1,23 @@++{++ "iss": "https://openvibe.network",++ "sub": "app:app_01K6R3A1B2C3D4E5F6G7H8J9KD",++ "actor_type": "app",++ "aud": [++ "openvibe.media"++ ],++ "cap": [],++ "ns": [++ "prj_01J0000000000000000000000Z",++ "app.prj_01J0000000000000000000000Z.*"++ ],++ "iat": 1790000000,++ "exp": 1790000300,++ "jti": "tok_abcdef21",++ "project_id": "prj_01J0000000000000000000000Z",++ "env": "production",++ "on_behalf_of": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",++ "consent": [++ "ai.train",++ "app.data"++ ]++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "sub": "42",++ "id": "42",++ "subject_id": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",++ "username": "ana",++ "client_id": "openvibe-live",++ "consent": [++ "ai.train",++ "ai.train"++ ],++ "iat": 1790000000,++ "exp": 1790003600++}nnew file mode 100644@@@ -0,0 +1,9 @@++{++ "sub": "42",++ "id": "42",++ "subject_id": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",++ "username": "ana",++ "client_id": "openvibe-live",++ "iat": 1790000000,++ "exp": 1790003600++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "sub": "42",++ "id": "42",++ "subject_id": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",++ "username": "ana",++ "client_id": "openvibe-live",++ "consent": "ai.train",++ "iat": 1790000000,++ "exp": 1790003600++}nnew file mode 100644@@@ -0,0 +1,12 @@++{++ "sub": "42",++ "id": "42",++ "subject_id": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",++ "username": "ana",++ "client_id": "openvibe-live",++ "consent": [++ "ai.looks"++ ],++ "iat": 1790000000,++ "exp": 1790003600++}nnew file mode 100644@@@ -0,0 +1,12 @@++{++ "sub": "42",++ "id": "42",++ "subject_id": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",++ "username": "ana",++ "client_id": "openvibe-live",++ "consent": [++ "data.sell"++ ],++ "iat": 1790000000,++ "exp": 1790003600++}nnew file mode 100644@@@ -0,0 +1,25 @@++{++ "iss": "https://openvibe.network",++ "sub": "app:app_01K6R3A1B2C3D4E5F6G7H8J9KD",++ "actor_type": "app",++ "aud": [++ "openvibe.media"++ ],++ "cap": [],++ "ns": [++ "prj_01J0000000000000000000000Z",++ "app.prj_01J0000000000000000000000Z.*"++ ],++ "iat": 1790000000,++ "exp": 1790000300,++ "jti": "tok_abcdef21",++ "project_id": "prj_01J0000000000000000000000Z",++ "env": "production",++ "on_behalf_of": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",++ "consent": [++ "ai.train",++ "data.public",++ "face.recognize",++ "app.data"++ ]++}nnew file mode 100644@@@ -0,0 +1,20 @@++{++ "iss": "https://openvibe.network",++ "sub": "app:app_01K6R3A1B2C3D4E5F6G7H8J9KD",++ "actor_type": "app",++ "aud": [++ "openvibe.media"++ ],++ "cap": [],++ "ns": [++ "prj_01J0000000000000000000000Z",++ "app.prj_01J0000000000000000000000Z.*"++ ],++ "iat": 1790000000,++ "exp": 1790000300,++ "jti": "tok_abcdef21",++ "project_id": "prj_01J0000000000000000000000Z",++ "env": "production",++ "on_behalf_of": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",++ "consent": []++}nnew file mode 100644@@@ -0,0 +1,12 @@++{++ "sub": "42",++ "id": "42",++ "subject_id": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",++ "username": "ana",++ "client_id": "openvibe-live",++ "consent": [++ "ai.train"++ ],++ "iat": 1790000000,++ "exp": 1790003600++}nnew file mode 100644@@@ -0,0 +1,14 @@++{++ "id": "cns_01K9A3A1B2C3D4E5F6G7H8J9KC",++ "subject": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",++ "purpose": "ai.train",++ "on": true,++ "kinds": [],++ "client": "app_01K6R3A1B2C3D4E5F6G7H8J9KD",++ "options": {},++ "text_version": "2026-11-07.1",++ "source": "oauth",++ "granted_at": "2026-11-07T05:00:00.000Z",++ "revoked_at": null,++ "expires_at": null++}nnew file mode 100644@@@ -0,0 +1,55 @@++{++ "purposes": [++ {++ "id": "ai.train",++ "title": "Train AI on your content",++ "allows": "It may use your streams, videos, voice, chat, posts, pictures and code to train or improve its AI.",++ "cannot_undo": "A model it already trained keeps what it learned.",++ "adults_only": true,++ "text_version": "2026-11-07.1"++ },++ {++ "id": "app.data",++ "title": "Use your content",++ "allows": "It may read and use your streams, videos, posts and other content for its own features.",++ "cannot_undo": "A copy it already made stays with the app.",++ "adults_only": false,++ "text_version": "2026-11-07.1"++ }++ ],++ "apps": [++ {++ "client": "not an app id!",++ "name": "Clip Helper",++ "third_party": true,++ "granted": [++ {++ "consent_id": "cns_01K9A3A1B2C3D4E5F6G7H8J9KC",++ "purpose": "ai.train",++ "granted_at": "2026-11-07T05:00:00.000Z",++ "text_version": "2026-11-07.1"++ }++ ],++ "quiet": [++ {++ "purpose": "face.recognize",++ "refused_at": "2026-11-07T05:00:00.000Z",++ "quiet_until": "2026-12-07T05:00:00.000Z"++ }++ ]++ },++ {++ "client": "openvibe-live",++ "name": "OpenVibe.Live",++ "third_party": false,++ "granted": [],++ "quiet": [++ {++ "purpose": "data.public",++ "refused_at": "2026-11-06T05:00:00.000Z",++ "quiet_until": "2026-12-06T05:00:00.000Z"++ }++ ]++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,58 @@++{++ "purposes": [++ {++ "id": "ai.train",++ "title": "Train AI on your content",++ "allows": "It may use your streams, videos, voice, chat, posts, pictures and code to train or improve its AI.",++ "cannot_undo": "A model it already trained keeps what it learned.",++ "adults_only": true,++ "text_version": "2026-11-07.1"++ },++ {++ "id": "app.data",++ "title": "Use your content",++ "allows": "It may read and use your streams, videos, posts and other content for its own features.",++ "cannot_undo": "A copy it already made stays with the app.",++ "adults_only": false,++ "text_version": "2026-11-07.1"++ }++ ],++ "apps": [++ {++ "client": "app_01K6R3A1B2C3D4E5F6G7H8J9KD",++ "name": "Clip Helper",++ "third_party": true,++ "granted": [++ {++ "consent_id": "cns_01K9A3A1B2C3D4E5F6G7H8J9KC",++ "purpose": "ai.train",++ "granted_at": "2026-11-07T05:00:00.000Z",++ "text_version": "2026-11-07.1"++ }++ ],++ "quiet": [++ {++ "purpose": "face.recognize",++ "refused_at": "2026-11-07T05:00:00.000Z",++ "quiet_until": "2026-12-07T05:00:00.000Z"++ }++ ],++ "refused": [++ "face.recognize"++ ]++ },++ {++ "client": "openvibe-live",++ "name": "OpenVibe.Live",++ "third_party": false,++ "granted": [],++ "quiet": [++ {++ "purpose": "data.public",++ "refused_at": "2026-11-06T05:00:00.000Z",++ "quiet_until": "2026-12-06T05:00:00.000Z"++ }++ ]++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,54 @@++{++ "purposes": [++ {++ "id": "ai.train",++ "title": "Train AI on your content",++ "allows": "It may use your streams, videos, voice, chat, posts, pictures and code to train or improve its AI.",++ "cannot_undo": "A model it already trained keeps what it learned.",++ "adults_only": true,++ "text_version": "2026-11-07.1"++ },++ {++ "id": "app.data",++ "title": "Use your content",++ "allows": "It may read and use your streams, videos, posts and other content for its own features.",++ "cannot_undo": "A copy it already made stays with the app.",++ "adults_only": false,++ "text_version": "2026-11-07.1"++ }++ ],++ "apps": [++ {++ "client": "app_01K6R3A1B2C3D4E5F6G7H8J9KD",++ "name": "Clip Helper",++ "third_party": true,++ "granted": [++ {++ "consent_id": "cns_01K9A3A1B2C3D4E5F6G7H8J9KC",++ "purpose": "ai.train",++ "granted_at": "2026-11-07T05:00:00.000Z",++ "text_version": "2026-11-07.1"++ }++ ],++ "quiet": [++ {++ "purpose": "face.recognize",++ "refused_at": "2026-11-07T05:00:00.000Z"++ }++ ]++ },++ {++ "client": "openvibe-live",++ "name": "OpenVibe.Live",++ "third_party": false,++ "granted": [],++ "quiet": [++ {++ "purpose": "data.public",++ "refused_at": "2026-11-06T05:00:00.000Z",++ "quiet_until": "2026-12-06T05:00:00.000Z"++ }++ ]++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,55 @@++{++ "purposes": [++ {++ "id": "ai.train",++ "title": "Train AI on your content",++ "allows": "It may use your streams, videos, voice, chat, posts, pictures and code to train or improve its AI.",++ "cannot_undo": "A model it already trained keeps what it learned.",++ "adults_only": true,++ "text_version": "2026-11-07.1"++ },++ {++ "id": "app.data",++ "title": "Use your content",++ "allows": "It may read and use your streams, videos, posts and other content for its own features.",++ "cannot_undo": "A copy it already made stays with the app.",++ "adults_only": false,++ "text_version": "2026-11-07.1"++ }++ ],++ "apps": [++ {++ "client": "app_01K6R3A1B2C3D4E5F6G7H8J9KD",++ "name": "Clip Helper",++ "third_party": true,++ "granted": [++ {++ "consent_id": "cns_01K9A3A1B2C3D4E5F6G7H8J9KC",++ "purpose": "ai.looks",++ "granted_at": "2026-11-07T05:00:00.000Z",++ "text_version": "2026-11-07.1"++ }++ ],++ "quiet": [++ {++ "purpose": "face.recognize",++ "refused_at": "2026-11-07T05:00:00.000Z",++ "quiet_until": "2026-12-07T05:00:00.000Z"++ }++ ]++ },++ {++ "client": "openvibe-live",++ "name": "OpenVibe.Live",++ "third_party": false,++ "granted": [],++ "quiet": [++ {++ "purpose": "data.public",++ "refused_at": "2026-11-06T05:00:00.000Z",++ "quiet_until": "2026-12-06T05:00:00.000Z"++ }++ ]++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,21 @@++{++ "purposes": [++ {++ "id": "ai.train",++ "title": "Train AI on your content",++ "allows": "It may use your streams, videos, voice, chat, posts, pictures and code to train or improve its AI.",++ "cannot_undo": "A model it already trained keeps what it learned.",++ "adults_only": true,++ "text_version": "2026-11-07.1"++ },++ {++ "id": "app.data",++ "title": "Use your content",++ "allows": "It may read and use your streams, videos, posts and other content for its own features.",++ "cannot_undo": "A copy it already made stays with the app.",++ "adults_only": false,++ "text_version": "2026-11-07.1"++ }++ ],++ "apps": []++}nnew file mode 100644@@@ -0,0 +1,55 @@++{++ "purposes": [++ {++ "id": "ai.train",++ "title": "Train AI on your content",++ "allows": "It may use your streams, videos, voice, chat, posts, pictures and code to train or improve its AI.",++ "cannot_undo": "A model it already trained keeps what it learned.",++ "adults_only": true,++ "text_version": "2026-11-07.1"++ },++ {++ "id": "app.data",++ "title": "Use your content",++ "allows": "It may read and use your streams, videos, posts and other content for its own features.",++ "cannot_undo": "A copy it already made stays with the app.",++ "adults_only": false,++ "text_version": "2026-11-07.1"++ }++ ],++ "apps": [++ {++ "client": "app_01K6R3A1B2C3D4E5F6G7H8J9KD",++ "name": "Clip Helper",++ "third_party": true,++ "granted": [++ {++ "consent_id": "cns_01K9A3A1B2C3D4E5F6G7H8J9KC",++ "purpose": "ai.train",++ "granted_at": "2026-11-07T05:00:00.000Z",++ "text_version": "2026-11-07.1"++ }++ ],++ "quiet": [++ {++ "purpose": "face.recognize",++ "refused_at": "2026-11-07T05:00:00.000Z",++ "quiet_until": "2026-12-07T05:00:00.000Z"++ }++ ]++ },++ {++ "client": "openvibe-live",++ "name": "OpenVibe.Live",++ "third_party": false,++ "granted": [],++ "quiet": [++ {++ "purpose": "data.public",++ "refused_at": "2026-11-06T05:00:00.000Z",++ "quiet_until": "2026-12-06T05:00:00.000Z"++ }++ ]++ }++ ]++}@@@ -277,6 +277,20 @@"pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$","description": "The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Also present on a node token that asks for an audience where the machine acts for its owner (Agent Streams S1: openvibe.live and live.agent_stream.publish); absent on every other client_credentials token."},++ "consent": {++ "type": "array",++ "maxItems": 4,++ "uniqueItems": true,++ "items": {++ "enum": [++ "ai.train",++ "data.public",++ "face.recognize",++ "app.data"++ ]++ },++ "description": "App tokens from the authorization-code flow (0.310.0): the purposes the person (on_behalf_of) granted this app at sign-in and that are still active when the token is issued, the claim network.consent-claim@1 defines. Present when the sign-in asked for consent:<purpose> scopes, [] when none is granted, absent otherwise."++ },"act": {"type": "object","required": [@@@ -92745,7 +92759,7 @@"null"],"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",-- "description": "The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of an app the person granted (required for app.data, and only there)."++ "description": "The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of the app the person granted it to. app.data always names one. Since 0.310.0 an app may ask for any purpose at sign-in (consent:<purpose>, network.consent-claim@1): the grant is one record per purpose with this client and source oauth, and it allows that app only, never OpenVibe itself or another app."},"options": {"type": "object",@@@ -92784,7 +92798,7 @@"ai.looks"]},-- "description": "app.data only: what the app may do with the person's content, as the consent screen showed it."++ "description": "app.data only, and not written since 0.310.0: an app's consent screen grants each purpose as its own record (client set), so a reader asks for the purpose itself. Kept so older records still validate."}},"description": "What the purpose needs beyond on and kinds. An empty object for ai.train and ai.looks."@@@ -92801,7 +92815,7 @@"api","import:vision"],-- "description": "Where the choice was made: the account page, an app's consent screen, the person's own API call, or OpenVibe.Vision's older face consents moved into Network."++ "description": "Where the choice was made: the account page, an app's consent screen at sign-in (consent:<purpose>, with client set), the person's own API call, or OpenVibe.Vision's older face consents moved into Network."},"granted_at": {"type": "string",@@@ -93068,6 +93082,210 @@}}},++ "network.consent-claim": {++ "version": "1.0.0",++ "owner": "network",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/network/consent-claim.v1.json",++ "title": "NetworkConsentClaim",++ "description": "network.consent-claim@1 (the consent plan's C4, 2026-11-07): the purposes a person granted to one app at sign-in, as the `consent` claim of the tokens OpenVibe.Network issues to that app. An app asks with one scope `consent:<purpose>` per purpose, beside its other scopes, in /oauth/authorize. The purposes an app may ask for are network.consent@1's except the retired ai.looks: ai.train, data.public, face.recognize and app.data. Any other `consent:` scope is refused with invalid_scope. The consent screen lists each purpose in plain words, off until the person ticks it, and the person can sign in with every purpose refused. A granted purpose is a network.consent@1 record with the app's OAuth client id as client and source oauth; the person turns it off on their account page (DELETE /api/v1/me/consent/:purpose/:client). A refused purpose is not asked again by that app for 30 days, unless the person asks for it again on their account page. The schema is the claims object of any token that carries the claim: a first-party user access token, a Dock token, or an app token (identity.service-token-claims@1).",++ "type": "object",++ "required": [++ "consent"++ ],++ "additionalProperties": true,++ "properties": {++ "consent": {++ "type": "array",++ "maxItems": 4,++ "uniqueItems": true,++ "items": {++ "enum": [++ "ai.train",++ "data.public",++ "face.recognize",++ "app.data"++ ]++ },++ "description": "Exactly the purposes this sign-in asked for that the person has granted to this app and that are active when the token is issued, in network.consent@1's order. Never a refused or unasked purpose and never another app's grant. An empty list means none is granted: the app learns \"not granted\", never whether the person refused or was not asked. The claim is absent when the sign-in asked for no consent scope. Every new token works it out again, so a purpose the person turns off is gone from the next one (a refresh, or the next code); a token already issued keeps it until it expires. A service about to use a person's content for an app still asks POST /internal/consent/check with the app as client. The token response's `scope` lists consent:<purpose> only for the purposes granted."++ }++ }++ }++ },++ "network.me-consent-apps-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/me-consent-apps-result.v1.json",++ "title": "NetworkMeConsentAppsResult",++ "description": "network.me-consent-apps-result@1 (the consent plan's C4, 2026-11-07): \"Apps you allowed\" on the signed-in person's account page. GET /api/v1/me/consent/apps on OpenVibe.Network. It lists every app the person granted a purpose to on its sign-in screen (consent:<purpose>, network.consent-claim@1), with the purposes granted and those refused in the last 30 days. It also carries the words the app consent screen shows for each purpose. Only the person's own session reads it; an app never sees this list, its own entry included.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "purposes",++ "apps"++ ],++ "properties": {++ "purposes": {++ "type": "array",++ "maxItems": 8,++ "description": "The purposes an app may ask for, with the exact words the app consent screen shows.",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "title",++ "allows",++ "cannot_undo",++ "adults_only",++ "text_version"++ ],++ "properties": {++ "id": {++ "enum": [++ "ai.train",++ "data.public",++ "face.recognize",++ "app.data"++ ]++ },++ "title": {++ "type": "string",++ "minLength": 1,++ "maxLength": 80++ },++ "allows": {++ "type": "string",++ "minLength": 1,++ "maxLength": 300,++ "description": "What ticking it allows the app, in one plain line."++ },++ "cannot_undo": {++ "type": "string",++ "maxLength": 300,++ "description": "What turning it off later cannot undo (empty when nothing)."++ },++ "adults_only": {++ "type": "boolean",++ "description": "Ticking it asks the person to confirm they are 18 or older."++ },++ "text_version": {++ "type": "string",++ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$"++ }++ }++ }++ },++ "apps": {++ "type": "array",++ "maxItems": 200,++ "description": "One entry per app with a purpose granted or refused, apps with an active grant first, then by name.",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "client",++ "name",++ "third_party",++ "granted",++ "quiet"++ ],++ "properties": {++ "client": {++ "type": "string",++ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",++ "description": "The app's OAuth client id."++ },++ "name": {++ "type": "string",++ "minLength": 1,++ "maxLength": 120,++ "description": "The app's name as its developer (or OpenVibe, for its own sites) registered it."++ },++ "third_party": {++ "type": "boolean",++ "description": "True for a developer's app (app_<ULID>), false for an OpenVibe site."++ },++ "granted": {++ "type": "array",++ "maxItems": 4,++ "description": "The purposes the app holds now (active network.consent@1 records with this client).",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "consent_id",++ "purpose",++ "granted_at",++ "text_version"++ ],++ "properties": {++ "consent_id": {++ "type": "string",++ "pattern": "^cns_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "purpose": {++ "enum": [++ "ai.train",++ "data.public",++ "face.recognize",++ "app.data"++ ]++ },++ "granted_at": {++ "type": "string",++ "format": "date-time"++ },++ "text_version": {++ "type": "string",++ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$"++ }++ }++ }++ },++ "quiet": {++ "type": "array",++ "maxItems": 4,++ "description": "The purposes the person refused this app in the last 30 days: the app does not ask for them again until quiet_until. DELETE /api/v1/me/consent/:purpose/:client/refusal ends that early, so the next sign-in asks.",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "purpose",++ "refused_at",++ "quiet_until"++ ],++ "properties": {++ "purpose": {++ "enum": [++ "ai.train",++ "data.public",++ "face.recognize",++ "app.data"++ ]++ },++ "refused_at": {++ "type": "string",++ "format": "date-time"++ },++ "quiet_until": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ }++ }++ }++ }++ },"network.consent-check-request": {"version": "1.0.0","owner": "network",@@@ -93110,7 +93328,7 @@"client": {"type": "string","pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",-- "description": "app.data only: the app asking. Required for app.data."++ "description": "The app the use is for. Required for app.data. For any other purpose it asks about that app's own grant from its sign-in (consent:<purpose>, 0.310.0); without it the answer is about OpenVibe's own use. One app's grant never answers for OpenVibe or for another app."},"feature": {"enum": [@@@ -93143,7 +93361,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/network/consent-check-result.v1.json","title": "NetworkConsentCheckResult",-- "description": "network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.",++ "description": "network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An app's grant (client set) answers only a check that names that app. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.","type": "object","additionalProperties": false,"required": [@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Actor API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.AI API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Bench API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Bot API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Domains API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Food API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.310.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"@@@ -145,7 +145,7 @@"service": "network","name": "OpenVibe.Network","origin": "https://openvibe.network",-- "operations": 60,++ "operations": 62,"capabilities": 35},{@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Inventory API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Live API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.MediaHub API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.310.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"@@@ -658,7 +658,7 @@"get": {"operationId": "get_api_v1_me_consent","summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",-- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client revokes one app. Off is the default for every purpose. Only the person's own session or token reaches it.",++ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.","tags": ["network.consent"],@@@ -790,7 +790,7 @@"put": {"operationId": "put_api_v1_me_consent_purpose","summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",-- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client revokes one app. Off is the default for every purpose. Only the person's own session or token reaches it.",++ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.","tags": ["network.consent"],@@@ -856,7 +856,7 @@"delete": {"operationId": "delete_api_v1_me_consent_purpose_client","summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",-- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client revokes one app. Off is the default for every purpose. Only the person's own session or token reaches it.",++ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.","tags": ["network.consent"],@@@ -1000,6 +1000,286 @@}}},++ "/api/v1/me/consent/{purpose}/{client}/refusal": {++ "delete": {++ "operationId": "delete_api_v1_me_consent_purpose_client_refusal",++ "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",++ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",++ "tags": [++ "network.consent"++ ],++ "x-openvibe-capabilities": [++ "network.consent.manage"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "network.consent.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/network.consent-choice.v1"++ },++ "parameters": [++ {++ "name": "purpose",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "client",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "on",++ "in": "query",++ "required": true,++ "schema": {++ "type": "boolean"++ }++ },++ {++ "name": "kinds",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 8,++ "uniqueItems": true,++ "items": {++ "enum": [++ "streams",++ "vods",++ "clips",++ "voice",++ "chat",++ "posts",++ "pictures",++ "code"++ ]++ }++ }++ },++ {++ "name": "options",++ "in": "query",++ "required": false,++ "schema": {++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "licence": {++ "enum": [++ "cc-by-4.0",++ "cc0-1.0"++ ]++ },++ "features": {++ "type": "array",++ "maxItems": 4,++ "uniqueItems": true,++ "items": {++ "enum": [++ "verify",++ "find",++ "cast",++ "library"++ ]++ }++ }++ }++ }++ },++ {++ "name": "text_version",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",++ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."++ },++ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."++ },++ {++ "name": "adult",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."++ },++ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/network.me-consent-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/me/consent/apps": {++ "get": {++ "operationId": "get_api_v1_me_consent_apps",++ "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",++ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",++ "tags": [++ "network.consent"++ ],++ "x-openvibe-capabilities": [++ "network.consent.manage"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "network.consent.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/network.consent-choice.v1"++ },++ "parameters": [++ {++ "name": "on",++ "in": "query",++ "required": true,++ "schema": {++ "type": "boolean"++ }++ },++ {++ "name": "kinds",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 8,++ "uniqueItems": true,++ "items": {++ "enum": [++ "streams",++ "vods",++ "clips",++ "voice",++ "chat",++ "posts",++ "pictures",++ "code"++ ]++ }++ }++ },++ {++ "name": "options",++ "in": "query",++ "required": false,++ "schema": {++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "licence": {++ "enum": [++ "cc-by-4.0",++ "cc0-1.0"++ ]++ },++ "features": {++ "type": "array",++ "maxItems": 4,++ "uniqueItems": true,++ "items": {++ "enum": [++ "verify",++ "find",++ "cast",++ "library"++ ]++ }++ }++ }++ }++ },++ {++ "name": "text_version",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",++ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."++ },++ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."++ },++ {++ "name": "adult",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."++ },++ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/network.me-consent-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/api/v1/node/self/capabilities": {"put": {"operationId": "put_api_v1_node_self_capabilities",@@@ -4735,7 +5015,7 @@"client": {"type": "string","pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",-- "description": "app.data only: the app asking. Required for app.data."++ "description": "The app the use is for. Required for app.data. For any other purpose it asks about that app's own grant from its sign-in (consent:<purpose>, 0.310.0); without it the answer is about OpenVibe's own use. One app's grant never answers for OpenVibe or for another app."},"feature": {"enum": [@@@ -4761,7 +5041,7 @@},"network.consent-check-result.v1": {"title": "NetworkConsentCheckResult",-- "description": "network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.",++ "description": "network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An app's grant (client set) answers only a check that names that app. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.","type": "object","additionalProperties": false,"required": [@@@ -4967,7 +5247,7 @@"null"],"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",-- "description": "The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of an app the person granted (required for app.data, and only there)."++ "description": "The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of the app the person granted it to. app.data always names one. Since 0.310.0 an app may ask for any purpose at sign-in (consent:<purpose>, network.consent-claim@1): the grant is one record per purpose with this client and source oauth, and it allows that app only, never OpenVibe itself or another app."},"options": {"type": "object",@@@ -5006,7 +5286,7 @@"ai.looks"]},-- "description": "app.data only: what the app may do with the person's content, as the consent screen showed it."++ "description": "app.data only, and not written since 0.310.0: an app's consent screen grants each purpose as its own record (client set), so a reader asks for the purpose itself. Kept so older records still validate."}},"description": "What the purpose needs beyond on and kinds. An empty object for ai.train and ai.looks."@@@ -5023,7 +5303,7 @@"api","import:vision"],-- "description": "Where the choice was made: the account page, an app's consent screen, the person's own API call, or OpenVibe.Vision's older face consents moved into Network."++ "description": "Where the choice was made: the account page, an app's consent screen at sign-in (consent:<purpose>, with client set), the person's own API call, or OpenVibe.Vision's older face consents moved into Network."},"granted_at": {"type": "string",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.News API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.310.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": "OpenRestream API",-- "version": "0.309.0",-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Play API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Quest API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Report API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Run API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Services API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Space API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Today API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Vision API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Watch API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Work API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Zone API",-- "version": "0.309.0",-- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",++ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.310.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"@@@ -95,6 +95,29 @@ export type ServiceTokenClaims = {* The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Also present on a node token that asks for an audience where the machine acts for its owner (Agent Streams S1: openvibe.live and live.agent_stream.publish); absent on every other client_credentials token.*/on_behalf_of?: string;++ /**++ * App tokens from the authorization-code flow (0.310.0): the purposes the person (on_behalf_of) granted this app at sign-in and that are still active when the token is issued, the claim network.consent-claim@1 defines. Present when the sign-in asked for consent:<purpose> scopes, [] when none is granted, absent otherwise.++ *++ * @maxItems 4++ */++ consent?:++ | []++ | ["ai.train" | "data.public" | "face.recognize" | "app.data"]++ | [++ "ai.train" | "data.public" | "face.recognize" | "app.data",++ "ai.train" | "data.public" | "face.recognize" | "app.data"++ ]++ | [++ "ai.train" | "data.public" | "face.recognize" | "app.data",++ "ai.train" | "data.public" | "face.recognize" | "app.data",++ "ai.train" | "data.public" | "face.recognize" | "app.data"++ ]++ | [++ "ai.train" | "data.public" | "face.recognize" | "app.data",++ "ai.train" | "data.public" | "face.recognize" | "app.data",++ "ai.train" | "data.public" | "face.recognize" | "app.data",++ "ai.train" | "data.public" | "face.recognize" | "app.data"++ ];/*** Agent tokens: the host that runs the agent and the token was minted for (RFC 8693 actor claim), a service or an app.*/@@@ -272002,7 +272025,7 @@ export interface NetworkConsent {"streams" | "vods" | "clips" | "voice" | "chat" | "posts" | "pictures" | "code"];/**-- * The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of an app the person granted (required for app.data, and only there).++ * The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of the app the person granted it to. app.data always names one. Since 0.310.0 an app may ask for any purpose at sign-in (consent:<purpose>, network.consent-claim@1): the grant is one record per purpose with this client and source oauth, and it allows that app only, never OpenVibe itself or another app.*/client: string | null;/**@@@ -272034,7 +272057,7 @@ export interface NetworkConsent {"verify" | "find" | "cast" | "library"];/**-- * app.data only: what the app may do with the person's content, as the consent screen showed it.++ * app.data only, and not written since 0.310.0: an app's consent screen grants each purpose as its own record (client set), so a reader asks for the purpose itself. Kept so older records still validate.** @maxItems 4*/@@@ -272062,7 +272085,7 @@ export interface NetworkConsent {*/text_version: string;/**-- * Where the choice was made: the account page, an app's consent screen, the person's own API call, or OpenVibe.Vision's older face consents moved into Network.++ * Where the choice was made: the account page, an app's consent screen at sign-in (consent:<purpose>, with client set), the person's own API call, or OpenVibe.Vision's older face consents moved into Network.*/source: "account" | "oauth" | "api" | "import:vision";/**@@@ -282846,6 +282869,838 @@ export interface NetworkMeConsentResult {}[];}++/** [email protected] (owner: network) */++/**++ * network.consent-claim@1 (the consent plan's C4, 2026-11-07): the purposes a person granted to one app at sign-in, as the `consent` claim of the tokens OpenVibe.Network issues to that app. An app asks with one scope `consent:<purpose>` per purpose, beside its other scopes, in /oauth/authorize. The purposes an app may ask for are network.consent@1's except the retired ai.looks: ai.train, data.public, face.recognize and app.data. Any other `consent:` scope is refused with invalid_scope. The consent screen lists each purpose in plain words, off until the person ticks it, and the person can sign in with every purpose refused. A granted purpose is a network.consent@1 record with the app's OAuth client id as client and source oauth; the person turns it off on their account page (DELETE /api/v1/me/consent/:purpose/:client). A refused purpose is not asked again by that app for 30 days, unless the person asks for it again on their account page. The schema is the claims object of any token that carries the claim: a first-party user access token, a Dock token, or an app token (identity.service-token-claims@1).++ */++export interface NetworkConsentClaim {++ /**++ * Exactly the purposes this sign-in asked for that the person has granted to this app and that are active when the token is issued, in network.consent@1's order. Never a refused or unasked purpose and never another app's grant. An empty list means none is granted: the app learns "not granted", never whether the person refused or was not asked. The claim is absent when the sign-in asked for no consent scope. Every new token works it out again, so a purpose the person turns off is gone from the next one (a refresh, or the next code); a token already issued keeps it until it expires. A service about to use a person's content for an app still asks POST /internal/consent/check with the app as client. The token response's `scope` lists consent:<purpose> only for the purposes granted.++ *++ * @maxItems 4++ */++ consent:++ | []++ | ["ai.train" | "data.public" | "face.recognize" | "app.data"]++ | [++ "ai.train" | "data.public" | "face.recognize" | "app.data",++ "ai.train" | "data.public" | "face.recognize" | "app.data"++ ]++ | [++ "ai.train" | "data.public" | "face.recognize" | "app.data",++ "ai.train" | "data.public" | "face.recognize" | "app.data",++ "ai.train" | "data.public" | "face.recognize" | "app.data"++ ]++ | [++ "ai.train" | "data.public" | "face.recognize" | "app.data",++ "ai.train" | "data.public" | "face.recognize" | "app.data",++ "ai.train" | "data.public" | "face.recognize" | "app.data",++ "ai.train" | "data.public" | "face.recognize" | "app.data"++ ];++ [k: string]: unknown | undefined;++}++++/** [email protected] (owner: network) */++/**++ * network.me-consent-apps-result@1 (the consent plan's C4, 2026-11-07): "Apps you allowed" on the signed-in person's account page. GET /api/v1/me/consent/apps on OpenVibe.Network. It lists every app the person granted a purpose to on its sign-in screen (consent:<purpose>, network.consent-claim@1), with the purposes granted and those refused in the last 30 days. It also carries the words the app consent screen shows for each purpose. Only the person's own session reads it; an app never sees this list, its own entry included.++ */++export interface NetworkMeConsentAppsResult {++ /**++ * The purposes an app may ask for, with the exact words the app consent screen shows.++ *++ * @maxItems 8++ */++ purposes:++ | []++ | [++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ }++ ]++ | [++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ }++ ]++ | [++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ }++ ]++ | [++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ }++ ]++ | [++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ }++ ]++ | [++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ }++ ]++ | [++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ }++ ]++ | [++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ },++ {++ id: "ai.train" | "data.public" | "face.recognize" | "app.data";++ title: string;++ /**++ * What ticking it allows the app, in one plain line.++ */++ allows: string;++ /**++ * What turning it off later cannot undo (empty when nothing).++ */++ cannot_undo: string;++ /**++ * Ticking it asks the person to confirm they are 18 or older.++ */++ adults_only: boolean;++ text_version: string;++ }++ ];++ /**++ * One entry per app with a purpose granted or refused, apps with an active grant first, then by name.++ *++ * @maxItems 200++ */++ apps: {++ /**++ * The app's OAuth client id.++ */++ client: string;++ /**++ * The app's name as its developer (or OpenVibe, for its own sites) registered it.++ */++ name: string;++ /**++ * True for a developer's app (app_<ULID>), false for an OpenVibe site.++ */++ third_party: boolean;++ /**++ * The purposes the app holds now (active network.consent@1 records with this client).++ *++ * @maxItems 4++ */++ granted:++ | []++ | [++ {++ consent_id: string;++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ granted_at: string;++ text_version: string;++ }++ ]++ | [++ {++ consent_id: string;++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ granted_at: string;++ text_version: string;++ },++ {++ consent_id: string;++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ granted_at: string;++ text_version: string;++ }++ ]++ | [++ {++ consent_id: string;++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ granted_at: string;++ text_version: string;++ },++ {++ consent_id: string;++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ granted_at: string;++ text_version: string;++ },++ {++ consent_id: string;++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ granted_at: string;++ text_version: string;++ }++ ]++ | [++ {++ consent_id: string;++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ granted_at: string;++ text_version: string;++ },++ {++ consent_id: string;++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ granted_at: string;++ text_version: string;++ },++ {++ consent_id: string;++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ granted_at: string;++ text_version: string;++ },++ {++ consent_id: string;++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ granted_at: string;++ text_version: string;++ }++ ];++ /**++ * The purposes the person refused this app in the last 30 days: the app does not ask for them again until quiet_until. DELETE /api/v1/me/consent/:purpose/:client/refusal ends that early, so the next sign-in asks.++ *++ * @maxItems 4++ */++ quiet:++ | []++ | [++ {++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ refused_at: string;++ quiet_until: string;++ }++ ]++ | [++ {++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ refused_at: string;++ quiet_until: string;++ },++ {++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ refused_at: string;++ quiet_until: string;++ }++ ]++ | [++ {++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ refused_at: string;++ quiet_until: string;++ },++ {++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ refused_at: string;++ quiet_until: string;++ },++ {++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ refused_at: string;++ quiet_until: string;++ }++ ]++ | [++ {++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ refused_at: string;++ quiet_until: string;++ },++ {++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ refused_at: string;++ quiet_until: string;++ },++ {++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ refused_at: string;++ quiet_until: string;++ },++ {++ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";++ refused_at: string;++ quiet_until: string;++ }++ ];++ }[];++}++/** [email protected] (owner: network) *//*** network.consent-check-request@1 (L24): may this purpose be used for these people? POST /internal/consent/check on OpenVibe.Network with a service token holding network.consent.read. One purpose per call, up to 500 subjects (a dataset build or a chat log checks its authors in batches).@@@ -282860,7 +283715,7 @@ export interface NetworkConsentCheckRequest {*/kind?: "streams" | "vods" | "clips" | "voice" | "chat" | "posts" | "pictures" | "code";/**-- * app.data only: the app asking. Required for app.data.++ * The app the use is for. Required for app.data. For any other purpose it asks about that app's own grant from its sign-in (consent:<purpose>, 0.310.0); without it the answer is about OpenVibe's own use. One app's grant never answers for OpenVibe or for another app.*/client?: string;/**@@@ -282878,7 +283733,7 @@ export interface NetworkConsentCheckRequest {/** [email protected] (owner: network) *//**-- * network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.++ * network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An app's grant (client set) answers only a check that names that app. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.*/export interface NetworkConsentCheckResult {purpose: "ai.train" | "data.public" | "face.recognize" | "ai.looks" | "app.data";@@@ -4,7 +4,7 @@"owner": "network","status": "active","visibility": "public",-- "description": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client revokes one app. Off is the default for every purpose. Only the person's own session or token reaches it.",++ "description": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.","inputSchema": "network.consent-choice@1","outputSchema": "network.me-consent-result@1","permissions": [@@@ -21,6 +21,8 @@"implementedBy": ["GET /api/v1/me/consent","PUT /api/v1/me/consent/:purpose",-- "DELETE /api/v1/me/consent/:purpose/:client"++ "DELETE /api/v1/me/consent/:purpose/:client",++ "GET /api/v1/me/consent/apps",++ "DELETE /api/v1/me/consent/:purpose/:client/refusal"]}@@@ -1,12 +1,12 @@{"name": "openvibe-contracts",-- "version": "0.309.0",++ "version": "0.310.0","lockfileVersion": 3,"requires": true,"packages": {"": {"name": "openvibe-contracts",-- "version": "0.309.0",++ "version": "0.310.0","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.309.0",++ "version": "0.310.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",@@@ -57,6 +57,44 @@ for (const id of ['network.consent', 'network.consent-choice', 'network.me-conse}}++// Apps ask per purpose at sign-in (C4, 0.310.0): consent:<purpose> scopes and the `consent` claim. An app may ask for++// every purpose of the family except the retired ai.looks, and the claim, the app token's claim and the account page's++// "Apps you allowed" all list that same set.++const APP_PURPOSES = PURPOSES.filter((p) => p !== 'ai.looks');++const claim = schema('network/consent-claim.v1.json');++const tokenClaims = schema('identity/service-token-claims.v1.json');++const apps = schema('network/me-consent-apps-result.v1.json');++for (const [name, list] of [++ ['claim.consent', claim.properties.consent.items.enum],++ ['service-token.consent', tokenClaims.properties.consent.items.enum],++ ['apps.purposes.id', apps.properties.purposes.items.properties.id.enum],++ ['apps.granted.purpose', apps.properties.apps.items.properties.granted.items.properties.purpose.enum],++ ['apps.quiet.purpose', apps.properties.apps.items.properties.quiet.items.properties.purpose.enum],++]) assert.deepStrictEqual(list, APP_PURPOSES, `${name} lists the purposes an app may ask for`);++assert.strictEqual(claim.properties.consent.uniqueItems, true, 'a purpose is in the claim once');++assert.strictEqual(claim.properties.consent.maxItems, APP_PURPOSES.length);++// The claim carries what was granted, nothing about what was refused.++assert.deepStrictEqual(Object.keys(claim.properties), ['consent'], 'the claim names granted purposes only');++assert.ok(!('refused' in apps.properties.apps.items.properties), 'the account page calls a refusal quiet, with its end');++assert.strictEqual(apps.properties.apps.items.additionalProperties, false);++// An app's grant is a consent record like any other: client set, source oauth.++const appGrant = fx('network.consent', 'valid', 'oauth-app-train.json');++assert.strictEqual(appGrant.source, 'oauth');++assert.ok(appGrant.client && appGrant.purpose !== 'app.data', 'an app holds a purpose other than app.data under its own client id');++// The account page reaches the new routes through the same capability.++const manage = JSON.parse(fs.readFileSync(path.join(ROOT, 'manifests', 'capabilities', 'network.consent.manage.json'), 'utf8'));++for (const route of ['GET /api/v1/me/consent/apps', 'DELETE /api/v1/me/consent/:purpose/:client/refusal']) {++ assert.ok(manage.implementedBy.includes(route), `network.consent.manage names ${route}`);++}++for (const id of ['network.consent-claim', 'network.me-consent-apps-result']) {++ for (const kind of ['valid', 'invalid']) {++ for (const f of fs.readdirSync(path.join(ROOT, 'fixtures', id, kind))) {++ const r = c.validate(`${id}@1`, fx(id, kind, f));++ assert.strictEqual(r.valid, kind === 'valid', `${id} ${kind}/${f}: ${JSON.stringify(r.errors)}`);++ }++ }++}++// The change event names the change, never the content it covers.assert.strictEqual(changed.additionalProperties, false);assert.ok(!('content' in changed.properties) && !('text' in changed.properties), 'the event carries no content');@@@ -74,4 +112,4 @@ const network = JSON.parse(fs.readFileSync(path.join(ROOT, 'manifests', 'serviceassert.ok(network.capabilities.includes('network.consent.read') && network.capabilities.includes('network.consent.manage'));assert.ok(network.eventsProduced.includes('network.consent.changed'));--console.log('network.consent@1: one list of purposes and kinds, cns_ ids, no selling purpose, fixtures, capabilities');++console.log('network.consent@1: one list of purposes and kinds, cns_ ids, no selling purpose, the consent claim and Apps you allowed, fixtures, capabilities');
@@ -1,5 +1,14 @@
@@ -1,5 +1,14 @@
# Changelog
# Changelog
+## 0.310.0 — 2026-11-07
+
+- Apps ask for consent per purpose at sign-in (the consent plan's C4). An app adds one scope `consent:<purpose>` per purpose to /oauth/authorize, beside its other scopes. It may ask for every purpose of the consent family except the retired `ai.looks`: `ai.train`, `data.public`, `face.recognize` and `app.data`. Any other `consent:` scope is refused with `invalid_scope`. The consent screen lists each purpose in plain words, nothing ticked, and the person can sign in with every purpose refused. A refused purpose is not asked again by that app for 30 days, unless the person asks for it again on their account page.
+- Add `network.consent-claim@1`: the `consent` claim of the tokens Network issues from that sign-in (a first-party user token, a Dock token or an app token). It holds exactly the purposes the sign-in asked for that the person granted to that app and that are active when the token is issued, `[]` when none. It never holds a refused purpose or another app's grant, and the app learns only "not granted". The claim is absent when no consent scope was asked. Every new token works it out again, so a purpose turned off is gone from the next one.
+- `identity.service-token-claims@1` gains the optional `consent` claim (additive): an app token from the authorization-code flow carries it when the sign-in asked for consent scopes.
+- Add `network.me-consent-apps-result@1`: "Apps you allowed" on the account page (`GET /api/v1/me/consent/apps`), listing each app's granted purposes and the ones refused in the last 30 days (`quiet`, with the day it ends), with the words the app consent screen shows. `network.consent.manage` names that route and `DELETE /api/v1/me/consent/:purpose/:client/refusal`, which ends a refusal's quiet days so the app's next sign-in asks again.
+- `network.consent@1`: an app's grant is one record per purpose, with the app as `client` and `source` `oauth`. `client` is no longer only for `app.data`, and `options.purposes` is not written any more (kept so older records validate). `network.consent-check-request@1`'s `client` may name the app for any purpose: one app's grant never answers for OpenVibe itself or for another app. Descriptions only: no shape changes.
+- Valid and invalid fixtures cover a user token with one purpose, an app token with none and with all four, a retired, unknown, duplicated or missing claim, a claim that is not a list, an app's grant record, the account page with two apps and with none, a bad client id, a retired purpose, an extra key and a quiet refusal with no end.
+
## 0.309.0 — 2026-11-07
## 0.309.0 — 2026-11-07
- Add `bot.machine-pool@1` (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC (`rpc-server` on the members, `llama-server --rpc` on the head). OpenVibe.Network holds the record: the owner proposes it on /machines with two to eight of their own machines (the default head has the most free VRAM), and each machine reads it with its own node token and approves it on itself. A member's `certificate` is null until it approves; then it is that machine's self-signed Ed25519 public certificate, which the other members pin for the pool's private link. The private key stays on the machine: a certificate slot holding anything with `PRIVATE KEY` in it is refused, and so is any field the schema does not name. A member's `address` is a private RFC 1918 address and prefix (never 0.0.0.0, loopback or a public address) and its `public_ip` is the address Network saw it from. The rules a schema cannot say are written in the description and checked by Network and Node: the head is a member, every member is the owner's today, all share one public address and one private subnet, the head's direct link probe to each passes, every key is its own, and any change of membership, owner, head or key revokes the pool so every machine approves a new one.
- Add `bot.machine-pool@1` (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC (`rpc-server` on the members, `llama-server --rpc` on the head). OpenVibe.Network holds the record: the owner proposes it on /machines with two to eight of their own machines (the default head has the most free VRAM), and each machine reads it with its own node token and approves it on itself. A member's `certificate` is null until it approves; then it is that machine's self-signed Ed25519 public certificate, which the other members pin for the pool's private link. The private key stays on the machine: a certificate slot holding anything with `PRIVATE KEY` in it is refused, and so is any field the schema does not name. A member's `address` is a private RFC 1918 address and prefix (never 0.0.0.0, loopback or a public address) and its `public_ip` is the address Network saw it from. The rules a schema cannot say are written in the description and checked by Network and Node: the head is a member, every member is the owner's today, all share one public address and one private subnet, the head's direct link probe to each passes, every key is its own, and any change of membership, owner, head or key revokes the pool so every machine approves a new one.
@@ -8323,6 +8323,24 @@
@@ -8323,6 +8323,24 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "network.consent-claim",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "network/consent-claim.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "network.me-consent-apps-result",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "network/me-consent-apps-result.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "network.consent-check-request",
"id": "network.consent-check-request",
"version": "1.0.0",
"version": "1.0.0",
@@ -92,6 +92,20 @@
@@ -92,6 +92,20 @@
"pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
"pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Also present on a node token that asks for an audience where the machine acts for its owner (Agent Streams S1: openvibe.live and live.agent_stream.publish); absent on every other client_credentials token."
"description": "The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Also present on a node token that asks for an audience where the machine acts for its owner (Agent Streams S1: openvibe.live and live.agent_stream.publish); absent on every other client_credentials token."
},
},
+ "consent": {
+ "type": "array",
+ "maxItems": 4,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "ai.train",
+ "data.public",
+ "face.recognize",
+ "app.data"
+ ]
+ },
+ "description": "App tokens from the authorization-code flow (0.310.0): the purposes the person (on_behalf_of) granted this app at sign-in and that are still active when the token is issued, the claim network.consent-claim@1 defines. Present when the sign-in asked for consent:<purpose> scopes, [] when none is granted, absent otherwise."
+ },
"act": {
"act": {
"type": "object",
"type": "object",
"required": [
"required": [
@@ -18,7 +18,7 @@
@@ -18,7 +18,7 @@
"client": {
"client": {
"type": "string",
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
- "description": "app.data only: the app asking. Required for app.data."
+ "description": "The app the use is for. Required for app.data. For any other purpose it asks about that app's own grant from its sign-in (consent:<purpose>, 0.310.0); without it the answer is about OpenVibe's own use. One app's grant never answers for OpenVibe or for another app."
},
},
"feature": {
"feature": {
"enum": ["verify", "find", "cast", "library"],
"enum": ["verify", "find", "cast", "library"],
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/network/consent-check-result.v1.json",
"$id": "https://openvibe.network/contracts/network/consent-check-result.v1.json",
"title": "NetworkConsentCheckResult",
"title": "NetworkConsentCheckResult",
- "description": "network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.",
+ "description": "network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An app's grant (client set) answers only a check that names that app. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
"required": ["purpose", "allowed", "checked_at"],
"required": ["purpose", "allowed", "checked_at"],
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/consent-claim.v1.json",
+ "title": "NetworkConsentClaim",
+ "description": "network.consent-claim@1 (the consent plan's C4, 2026-11-07): the purposes a person granted to one app at sign-in, as the `consent` claim of the tokens OpenVibe.Network issues to that app. An app asks with one scope `consent:<purpose>` per purpose, beside its other scopes, in /oauth/authorize. The purposes an app may ask for are network.consent@1's except the retired ai.looks: ai.train, data.public, face.recognize and app.data. Any other `consent:` scope is refused with invalid_scope. The consent screen lists each purpose in plain words, off until the person ticks it, and the person can sign in with every purpose refused. A granted purpose is a network.consent@1 record with the app's OAuth client id as client and source oauth; the person turns it off on their account page (DELETE /api/v1/me/consent/:purpose/:client). A refused purpose is not asked again by that app for 30 days, unless the person asks for it again on their account page. The schema is the claims object of any token that carries the claim: a first-party user access token, a Dock token, or an app token (identity.service-token-claims@1).",
+ "type": "object",
+ "required": ["consent"],
+ "additionalProperties": true,
+ "properties": {
+ "consent": {
+ "type": "array",
+ "maxItems": 4,
+ "uniqueItems": true,
+ "items": { "enum": ["ai.train", "data.public", "face.recognize", "app.data"] },
+ "description": "Exactly the purposes this sign-in asked for that the person has granted to this app and that are active when the token is issued, in network.consent@1's order. Never a refused or unasked purpose and never another app's grant. An empty list means none is granted: the app learns \"not granted\", never whether the person refused or was not asked. The claim is absent when the sign-in asked for no consent scope. Every new token works it out again, so a purpose the person turns off is gone from the next one (a refresh, or the next code); a token already issued keeps it until it expires. A service about to use a person's content for an app still asks POST /internal/consent/check with the app as client. The token response's `scope` lists consent:<purpose> only for the purposes granted."
+ }
+ }
+}
@@ -35,7 +35,7 @@
@@ -35,7 +35,7 @@
"client": {
"client": {
"type": ["string", "null"],
"type": ["string", "null"],
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
- "description": "The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of an app the person granted (required for app.data, and only there)."
+ "description": "The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of the app the person granted it to. app.data always names one. Since 0.310.0 an app may ask for any purpose at sign-in (consent:<purpose>, network.consent-claim@1): the grant is one record per purpose with this client and source oauth, and it allows that app only, never OpenVibe itself or another app."
},
},
"options": {
"options": {
"type": "object",
"type": "object",
@@ -57,7 +57,7 @@
@@ -57,7 +57,7 @@
"maxItems": 4,
"maxItems": 4,
"uniqueItems": true,
"uniqueItems": true,
"items": { "enum": ["ai.train", "data.public", "face.recognize", "ai.looks"] },
"items": { "enum": ["ai.train", "data.public", "face.recognize", "ai.looks"] },
- "description": "app.data only: what the app may do with the person's content, as the consent screen showed it."
+ "description": "app.data only, and not written since 0.310.0: an app's consent screen grants each purpose as its own record (client set), so a reader asks for the purpose itself. Kept so older records still validate."
}
}
},
},
"description": "What the purpose needs beyond on and kinds. An empty object for ai.train and ai.looks."
"description": "What the purpose needs beyond on and kinds. An empty object for ai.train and ai.looks."
@@ -69,7 +69,7 @@
@@ -69,7 +69,7 @@
},
},
"source": {
"source": {
"enum": ["account", "oauth", "api", "import:vision"],
"enum": ["account", "oauth", "api", "import:vision"],
- "description": "Where the choice was made: the account page, an app's consent screen, the person's own API call, or OpenVibe.Vision's older face consents moved into Network."
+ "description": "Where the choice was made: the account page, an app's consent screen at sign-in (consent:<purpose>, with client set), the person's own API call, or OpenVibe.Vision's older face consents moved into Network."
},
},
"granted_at": {
"granted_at": {
"type": "string",
"type": "string",
new file mode 100644
new file mode 100644
@@ -0,0 +1,75 @@
@@ -0,0 +1,75 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/network/me-consent-apps-result.v1.json",
+ "title": "NetworkMeConsentAppsResult",
+ "description": "network.me-consent-apps-result@1 (the consent plan's C4, 2026-11-07): \"Apps you allowed\" on the signed-in person's account page. GET /api/v1/me/consent/apps on OpenVibe.Network. It lists every app the person granted a purpose to on its sign-in screen (consent:<purpose>, network.consent-claim@1), with the purposes granted and those refused in the last 30 days. It also carries the words the app consent screen shows for each purpose. Only the person's own session reads it; an app never sees this list, its own entry included.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["purposes", "apps"],
+ "properties": {
+ "purposes": {
+ "type": "array",
+ "maxItems": 8,
+ "description": "The purposes an app may ask for, with the exact words the app consent screen shows.",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["id", "title", "allows", "cannot_undo", "adults_only", "text_version"],
+ "properties": {
+ "id": { "enum": ["ai.train", "data.public", "face.recognize", "app.data"] },
+ "title": { "type": "string", "minLength": 1, "maxLength": 80 },
+ "allows": { "type": "string", "minLength": 1, "maxLength": 300, "description": "What ticking it allows the app, in one plain line." },
+ "cannot_undo": { "type": "string", "maxLength": 300, "description": "What turning it off later cannot undo (empty when nothing)." },
+ "adults_only": { "type": "boolean", "description": "Ticking it asks the person to confirm they are 18 or older." },
+ "text_version": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$" }
+ }
+ }
+ },
+ "apps": {
+ "type": "array",
+ "maxItems": 200,
+ "description": "One entry per app with a purpose granted or refused, apps with an active grant first, then by name.",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["client", "name", "third_party", "granted", "quiet"],
+ "properties": {
+ "client": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$", "description": "The app's OAuth client id." },
+ "name": { "type": "string", "minLength": 1, "maxLength": 120, "description": "The app's name as its developer (or OpenVibe, for its own sites) registered it." },
+ "third_party": { "type": "boolean", "description": "True for a developer's app (app_<ULID>), false for an OpenVibe site." },
+ "granted": {
+ "type": "array",
+ "maxItems": 4,
+ "description": "The purposes the app holds now (active network.consent@1 records with this client).",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["consent_id", "purpose", "granted_at", "text_version"],
+ "properties": {
+ "consent_id": { "type": "string", "pattern": "^cns_[0-9A-HJKMNP-TV-Z]{26}$" },
+ "purpose": { "enum": ["ai.train", "data.public", "face.recognize", "app.data"] },
+ "granted_at": { "type": "string", "format": "date-time" },
+ "text_version": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$" }
+ }
+ }
+ },
+ "quiet": {
+ "type": "array",
+ "maxItems": 4,
+ "description": "The purposes the person refused this app in the last 30 days: the app does not ask for them again until quiet_until. DELETE /api/v1/me/consent/:purpose/:client/refusal ends that early, so the next sign-in asks.",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["purpose", "refused_at", "quiet_until"],
+ "properties": {
+ "purpose": { "enum": ["ai.train", "data.public", "face.recognize", "app.data"] },
+ "refused_at": { "type": "string", "format": "date-time" },
+ "quiet_until": { "type": "string", "format": "date-time" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "iss": "https://openvibe.network",
+ "sub": "app:app_01K6R3A1B2C3D4E5F6G7H8J9KD",
+ "actor_type": "app",
+ "aud": [
+ "openvibe.media"
+ ],
+ "cap": [],
+ "ns": [
+ "prj_01J0000000000000000000000Z",
+ "app.prj_01J0000000000000000000000Z.*"
+ ],
+ "iat": 1790000000,
+ "exp": 1790000300,
+ "jti": "tok_abcdef21",
+ "project_id": "prj_01J0000000000000000000000Z",
+ "env": "production",
+ "on_behalf_of": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",
+ "consent": [
+ "ai.looks"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
+{
+ "iss": "https://openvibe.network",
+ "sub": "app:app_01K6R3A1B2C3D4E5F6G7H8J9KD",
+ "actor_type": "app",
+ "aud": [
+ "openvibe.media"
+ ],
+ "cap": [],
+ "ns": [
+ "prj_01J0000000000000000000000Z",
+ "app.prj_01J0000000000000000000000Z.*"
+ ],
+ "iat": 1790000000,
+ "exp": 1790000300,
+ "jti": "tok_abcdef21",
+ "project_id": "prj_01J0000000000000000000000Z",
+ "env": "production",
+ "on_behalf_of": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",
+ "consent": [
+ "ai.train",
+ "app.data"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "sub": "42",
+ "id": "42",
+ "subject_id": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",
+ "username": "ana",
+ "client_id": "openvibe-live",
+ "consent": [
+ "ai.train",
+ "ai.train"
+ ],
+ "iat": 1790000000,
+ "exp": 1790003600
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+{
+ "sub": "42",
+ "id": "42",
+ "subject_id": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",
+ "username": "ana",
+ "client_id": "openvibe-live",
+ "iat": 1790000000,
+ "exp": 1790003600
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "sub": "42",
+ "id": "42",
+ "subject_id": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",
+ "username": "ana",
+ "client_id": "openvibe-live",
+ "consent": "ai.train",
+ "iat": 1790000000,
+ "exp": 1790003600
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
+{
+ "sub": "42",
+ "id": "42",
+ "subject_id": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",
+ "username": "ana",
+ "client_id": "openvibe-live",
+ "consent": [
+ "ai.looks"
+ ],
+ "iat": 1790000000,
+ "exp": 1790003600
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
+{
+ "sub": "42",
+ "id": "42",
+ "subject_id": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",
+ "username": "ana",
+ "client_id": "openvibe-live",
+ "consent": [
+ "data.sell"
+ ],
+ "iat": 1790000000,
+ "exp": 1790003600
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
+{
+ "iss": "https://openvibe.network",
+ "sub": "app:app_01K6R3A1B2C3D4E5F6G7H8J9KD",
+ "actor_type": "app",
+ "aud": [
+ "openvibe.media"
+ ],
+ "cap": [],
+ "ns": [
+ "prj_01J0000000000000000000000Z",
+ "app.prj_01J0000000000000000000000Z.*"
+ ],
+ "iat": 1790000000,
+ "exp": 1790000300,
+ "jti": "tok_abcdef21",
+ "project_id": "prj_01J0000000000000000000000Z",
+ "env": "production",
+ "on_behalf_of": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",
+ "consent": [
+ "ai.train",
+ "data.public",
+ "face.recognize",
+ "app.data"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
+{
+ "iss": "https://openvibe.network",
+ "sub": "app:app_01K6R3A1B2C3D4E5F6G7H8J9KD",
+ "actor_type": "app",
+ "aud": [
+ "openvibe.media"
+ ],
+ "cap": [],
+ "ns": [
+ "prj_01J0000000000000000000000Z",
+ "app.prj_01J0000000000000000000000Z.*"
+ ],
+ "iat": 1790000000,
+ "exp": 1790000300,
+ "jti": "tok_abcdef21",
+ "project_id": "prj_01J0000000000000000000000Z",
+ "env": "production",
+ "on_behalf_of": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",
+ "consent": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
+{
+ "sub": "42",
+ "id": "42",
+ "subject_id": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",
+ "username": "ana",
+ "client_id": "openvibe-live",
+ "consent": [
+ "ai.train"
+ ],
+ "iat": 1790000000,
+ "exp": 1790003600
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
+{
+ "id": "cns_01K9A3A1B2C3D4E5F6G7H8J9KC",
+ "subject": "usr_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4",
+ "purpose": "ai.train",
+ "on": true,
+ "kinds": [],
+ "client": "app_01K6R3A1B2C3D4E5F6G7H8J9KD",
+ "options": {},
+ "text_version": "2026-11-07.1",
+ "source": "oauth",
+ "granted_at": "2026-11-07T05:00:00.000Z",
+ "revoked_at": null,
+ "expires_at": null
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
+{
+ "purposes": [
+ {
+ "id": "ai.train",
+ "title": "Train AI on your content",
+ "allows": "It may use your streams, videos, voice, chat, posts, pictures and code to train or improve its AI.",
+ "cannot_undo": "A model it already trained keeps what it learned.",
+ "adults_only": true,
+ "text_version": "2026-11-07.1"
+ },
+ {
+ "id": "app.data",
+ "title": "Use your content",
+ "allows": "It may read and use your streams, videos, posts and other content for its own features.",
+ "cannot_undo": "A copy it already made stays with the app.",
+ "adults_only": false,
+ "text_version": "2026-11-07.1"
+ }
+ ],
+ "apps": [
+ {
+ "client": "not an app id!",
+ "name": "Clip Helper",
+ "third_party": true,
+ "granted": [
+ {
+ "consent_id": "cns_01K9A3A1B2C3D4E5F6G7H8J9KC",
+ "purpose": "ai.train",
+ "granted_at": "2026-11-07T05:00:00.000Z",
+ "text_version": "2026-11-07.1"
+ }
+ ],
+ "quiet": [
+ {
+ "purpose": "face.recognize",
+ "refused_at": "2026-11-07T05:00:00.000Z",
+ "quiet_until": "2026-12-07T05:00:00.000Z"
+ }
+ ]
+ },
+ {
+ "client": "openvibe-live",
+ "name": "OpenVibe.Live",
+ "third_party": false,
+ "granted": [],
+ "quiet": [
+ {
+ "purpose": "data.public",
+ "refused_at": "2026-11-06T05:00:00.000Z",
+ "quiet_until": "2026-12-06T05:00:00.000Z"
+ }
+ ]
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,58 @@
@@ -0,0 +1,58 @@
+{
+ "purposes": [
+ {
+ "id": "ai.train",
+ "title": "Train AI on your content",
+ "allows": "It may use your streams, videos, voice, chat, posts, pictures and code to train or improve its AI.",
+ "cannot_undo": "A model it already trained keeps what it learned.",
+ "adults_only": true,
+ "text_version": "2026-11-07.1"
+ },
+ {
+ "id": "app.data",
+ "title": "Use your content",
+ "allows": "It may read and use your streams, videos, posts and other content for its own features.",
+ "cannot_undo": "A copy it already made stays with the app.",
+ "adults_only": false,
+ "text_version": "2026-11-07.1"
+ }
+ ],
+ "apps": [
+ {
+ "client": "app_01K6R3A1B2C3D4E5F6G7H8J9KD",
+ "name": "Clip Helper",
+ "third_party": true,
+ "granted": [
+ {
+ "consent_id": "cns_01K9A3A1B2C3D4E5F6G7H8J9KC",
+ "purpose": "ai.train",
+ "granted_at": "2026-11-07T05:00:00.000Z",
+ "text_version": "2026-11-07.1"
+ }
+ ],
+ "quiet": [
+ {
+ "purpose": "face.recognize",
+ "refused_at": "2026-11-07T05:00:00.000Z",
+ "quiet_until": "2026-12-07T05:00:00.000Z"
+ }
+ ],
+ "refused": [
+ "face.recognize"
+ ]
+ },
+ {
+ "client": "openvibe-live",
+ "name": "OpenVibe.Live",
+ "third_party": false,
+ "granted": [],
+ "quiet": [
+ {
+ "purpose": "data.public",
+ "refused_at": "2026-11-06T05:00:00.000Z",
+ "quiet_until": "2026-12-06T05:00:00.000Z"
+ }
+ ]
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
+{
+ "purposes": [
+ {
+ "id": "ai.train",
+ "title": "Train AI on your content",
+ "allows": "It may use your streams, videos, voice, chat, posts, pictures and code to train or improve its AI.",
+ "cannot_undo": "A model it already trained keeps what it learned.",
+ "adults_only": true,
+ "text_version": "2026-11-07.1"
+ },
+ {
+ "id": "app.data",
+ "title": "Use your content",
+ "allows": "It may read and use your streams, videos, posts and other content for its own features.",
+ "cannot_undo": "A copy it already made stays with the app.",
+ "adults_only": false,
+ "text_version": "2026-11-07.1"
+ }
+ ],
+ "apps": [
+ {
+ "client": "app_01K6R3A1B2C3D4E5F6G7H8J9KD",
+ "name": "Clip Helper",
+ "third_party": true,
+ "granted": [
+ {
+ "consent_id": "cns_01K9A3A1B2C3D4E5F6G7H8J9KC",
+ "purpose": "ai.train",
+ "granted_at": "2026-11-07T05:00:00.000Z",
+ "text_version": "2026-11-07.1"
+ }
+ ],
+ "quiet": [
+ {
+ "purpose": "face.recognize",
+ "refused_at": "2026-11-07T05:00:00.000Z"
+ }
+ ]
+ },
+ {
+ "client": "openvibe-live",
+ "name": "OpenVibe.Live",
+ "third_party": false,
+ "granted": [],
+ "quiet": [
+ {
+ "purpose": "data.public",
+ "refused_at": "2026-11-06T05:00:00.000Z",
+ "quiet_until": "2026-12-06T05:00:00.000Z"
+ }
+ ]
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
+{
+ "purposes": [
+ {
+ "id": "ai.train",
+ "title": "Train AI on your content",
+ "allows": "It may use your streams, videos, voice, chat, posts, pictures and code to train or improve its AI.",
+ "cannot_undo": "A model it already trained keeps what it learned.",
+ "adults_only": true,
+ "text_version": "2026-11-07.1"
+ },
+ {
+ "id": "app.data",
+ "title": "Use your content",
+ "allows": "It may read and use your streams, videos, posts and other content for its own features.",
+ "cannot_undo": "A copy it already made stays with the app.",
+ "adults_only": false,
+ "text_version": "2026-11-07.1"
+ }
+ ],
+ "apps": [
+ {
+ "client": "app_01K6R3A1B2C3D4E5F6G7H8J9KD",
+ "name": "Clip Helper",
+ "third_party": true,
+ "granted": [
+ {
+ "consent_id": "cns_01K9A3A1B2C3D4E5F6G7H8J9KC",
+ "purpose": "ai.looks",
+ "granted_at": "2026-11-07T05:00:00.000Z",
+ "text_version": "2026-11-07.1"
+ }
+ ],
+ "quiet": [
+ {
+ "purpose": "face.recognize",
+ "refused_at": "2026-11-07T05:00:00.000Z",
+ "quiet_until": "2026-12-07T05:00:00.000Z"
+ }
+ ]
+ },
+ {
+ "client": "openvibe-live",
+ "name": "OpenVibe.Live",
+ "third_party": false,
+ "granted": [],
+ "quiet": [
+ {
+ "purpose": "data.public",
+ "refused_at": "2026-11-06T05:00:00.000Z",
+ "quiet_until": "2026-12-06T05:00:00.000Z"
+ }
+ ]
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
+{
+ "purposes": [
+ {
+ "id": "ai.train",
+ "title": "Train AI on your content",
+ "allows": "It may use your streams, videos, voice, chat, posts, pictures and code to train or improve its AI.",
+ "cannot_undo": "A model it already trained keeps what it learned.",
+ "adults_only": true,
+ "text_version": "2026-11-07.1"
+ },
+ {
+ "id": "app.data",
+ "title": "Use your content",
+ "allows": "It may read and use your streams, videos, posts and other content for its own features.",
+ "cannot_undo": "A copy it already made stays with the app.",
+ "adults_only": false,
+ "text_version": "2026-11-07.1"
+ }
+ ],
+ "apps": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
+{
+ "purposes": [
+ {
+ "id": "ai.train",
+ "title": "Train AI on your content",
+ "allows": "It may use your streams, videos, voice, chat, posts, pictures and code to train or improve its AI.",
+ "cannot_undo": "A model it already trained keeps what it learned.",
+ "adults_only": true,
+ "text_version": "2026-11-07.1"
+ },
+ {
+ "id": "app.data",
+ "title": "Use your content",
+ "allows": "It may read and use your streams, videos, posts and other content for its own features.",
+ "cannot_undo": "A copy it already made stays with the app.",
+ "adults_only": false,
+ "text_version": "2026-11-07.1"
+ }
+ ],
+ "apps": [
+ {
+ "client": "app_01K6R3A1B2C3D4E5F6G7H8J9KD",
+ "name": "Clip Helper",
+ "third_party": true,
+ "granted": [
+ {
+ "consent_id": "cns_01K9A3A1B2C3D4E5F6G7H8J9KC",
+ "purpose": "ai.train",
+ "granted_at": "2026-11-07T05:00:00.000Z",
+ "text_version": "2026-11-07.1"
+ }
+ ],
+ "quiet": [
+ {
+ "purpose": "face.recognize",
+ "refused_at": "2026-11-07T05:00:00.000Z",
+ "quiet_until": "2026-12-07T05:00:00.000Z"
+ }
+ ]
+ },
+ {
+ "client": "openvibe-live",
+ "name": "OpenVibe.Live",
+ "third_party": false,
+ "granted": [],
+ "quiet": [
+ {
+ "purpose": "data.public",
+ "refused_at": "2026-11-06T05:00:00.000Z",
+ "quiet_until": "2026-12-06T05:00:00.000Z"
+ }
+ ]
+ }
+ ]
+}
@@ -277,6 +277,20 @@
@@ -277,6 +277,20 @@
"pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
"pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Also present on a node token that asks for an audience where the machine acts for its owner (Agent Streams S1: openvibe.live and live.agent_stream.publish); absent on every other client_credentials token."
"description": "The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Also present on a node token that asks for an audience where the machine acts for its owner (Agent Streams S1: openvibe.live and live.agent_stream.publish); absent on every other client_credentials token."
},
},
+ "consent": {
+ "type": "array",
+ "maxItems": 4,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "ai.train",
+ "data.public",
+ "face.recognize",
+ "app.data"
+ ]
+ },
+ "description": "App tokens from the authorization-code flow (0.310.0): the purposes the person (on_behalf_of) granted this app at sign-in and that are still active when the token is issued, the claim network.consent-claim@1 defines. Present when the sign-in asked for consent:<purpose> scopes, [] when none is granted, absent otherwise."
+ },
"act": {
"act": {
"type": "object",
"type": "object",
"required": [
"required": [
@@ -92745,7 +92759,7 @@
@@ -92745,7 +92759,7 @@
"null"
"null"
],
],
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
- "description": "The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of an app the person granted (required for app.data, and only there)."
+ "description": "The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of the app the person granted it to. app.data always names one. Since 0.310.0 an app may ask for any purpose at sign-in (consent:<purpose>, network.consent-claim@1): the grant is one record per purpose with this client and source oauth, and it allows that app only, never OpenVibe itself or another app."
},
},
"options": {
"options": {
"type": "object",
"type": "object",
@@ -92784,7 +92798,7 @@
@@ -92784,7 +92798,7 @@
"ai.looks"
"ai.looks"
]
]
},
},
- "description": "app.data only: what the app may do with the person's content, as the consent screen showed it."
+ "description": "app.data only, and not written since 0.310.0: an app's consent screen grants each purpose as its own record (client set), so a reader asks for the purpose itself. Kept so older records still validate."
}
}
},
},
"description": "What the purpose needs beyond on and kinds. An empty object for ai.train and ai.looks."
"description": "What the purpose needs beyond on and kinds. An empty object for ai.train and ai.looks."
@@ -92801,7 +92815,7 @@
@@ -92801,7 +92815,7 @@
"api",
"api",
"import:vision"
"import:vision"
],
],
- "description": "Where the choice was made: the account page, an app's consent screen, the person's own API call, or OpenVibe.Vision's older face consents moved into Network."
+ "description": "Where the choice was made: the account page, an app's consent screen at sign-in (consent:<purpose>, with client set), the person's own API call, or OpenVibe.Vision's older face consents moved into Network."
},
},
"granted_at": {
"granted_at": {
"type": "string",
"type": "string",
@@ -93068,6 +93082,210 @@
@@ -93068,6 +93082,210 @@
}
}
}
}
},
},
+ "network.consent-claim": {
+ "version": "1.0.0",
+ "owner": "network",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/network/consent-claim.v1.json",
+ "title": "NetworkConsentClaim",
+ "description": "network.consent-claim@1 (the consent plan's C4, 2026-11-07): the purposes a person granted to one app at sign-in, as the `consent` claim of the tokens OpenVibe.Network issues to that app. An app asks with one scope `consent:<purpose>` per purpose, beside its other scopes, in /oauth/authorize. The purposes an app may ask for are network.consent@1's except the retired ai.looks: ai.train, data.public, face.recognize and app.data. Any other `consent:` scope is refused with invalid_scope. The consent screen lists each purpose in plain words, off until the person ticks it, and the person can sign in with every purpose refused. A granted purpose is a network.consent@1 record with the app's OAuth client id as client and source oauth; the person turns it off on their account page (DELETE /api/v1/me/consent/:purpose/:client). A refused purpose is not asked again by that app for 30 days, unless the person asks for it again on their account page. The schema is the claims object of any token that carries the claim: a first-party user access token, a Dock token, or an app token (identity.service-token-claims@1).",
+ "type": "object",
+ "required": [
+ "consent"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "consent": {
+ "type": "array",
+ "maxItems": 4,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "ai.train",
+ "data.public",
+ "face.recognize",
+ "app.data"
+ ]
+ },
+ "description": "Exactly the purposes this sign-in asked for that the person has granted to this app and that are active when the token is issued, in network.consent@1's order. Never a refused or unasked purpose and never another app's grant. An empty list means none is granted: the app learns \"not granted\", never whether the person refused or was not asked. The claim is absent when the sign-in asked for no consent scope. Every new token works it out again, so a purpose the person turns off is gone from the next one (a refresh, or the next code); a token already issued keeps it until it expires. A service about to use a person's content for an app still asks POST /internal/consent/check with the app as client. The token response's `scope` lists consent:<purpose> only for the purposes granted."
+ }
+ }
+ }
+ },
+ "network.me-consent-apps-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/me-consent-apps-result.v1.json",
+ "title": "NetworkMeConsentAppsResult",
+ "description": "network.me-consent-apps-result@1 (the consent plan's C4, 2026-11-07): \"Apps you allowed\" on the signed-in person's account page. GET /api/v1/me/consent/apps on OpenVibe.Network. It lists every app the person granted a purpose to on its sign-in screen (consent:<purpose>, network.consent-claim@1), with the purposes granted and those refused in the last 30 days. It also carries the words the app consent screen shows for each purpose. Only the person's own session reads it; an app never sees this list, its own entry included.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "purposes",
+ "apps"
+ ],
+ "properties": {
+ "purposes": {
+ "type": "array",
+ "maxItems": 8,
+ "description": "The purposes an app may ask for, with the exact words the app consent screen shows.",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "title",
+ "allows",
+ "cannot_undo",
+ "adults_only",
+ "text_version"
+ ],
+ "properties": {
+ "id": {
+ "enum": [
+ "ai.train",
+ "data.public",
+ "face.recognize",
+ "app.data"
+ ]
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 80
+ },
+ "allows": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 300,
+ "description": "What ticking it allows the app, in one plain line."
+ },
+ "cannot_undo": {
+ "type": "string",
+ "maxLength": 300,
+ "description": "What turning it off later cannot undo (empty when nothing)."
+ },
+ "adults_only": {
+ "type": "boolean",
+ "description": "Ticking it asks the person to confirm they are 18 or older."
+ },
+ "text_version": {
+ "type": "string",
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$"
+ }
+ }
+ }
+ },
+ "apps": {
+ "type": "array",
+ "maxItems": 200,
+ "description": "One entry per app with a purpose granted or refused, apps with an active grant first, then by name.",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "client",
+ "name",
+ "third_party",
+ "granted",
+ "quiet"
+ ],
+ "properties": {
+ "client": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
+ "description": "The app's OAuth client id."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 120,
+ "description": "The app's name as its developer (or OpenVibe, for its own sites) registered it."
+ },
+ "third_party": {
+ "type": "boolean",
+ "description": "True for a developer's app (app_<ULID>), false for an OpenVibe site."
+ },
+ "granted": {
+ "type": "array",
+ "maxItems": 4,
+ "description": "The purposes the app holds now (active network.consent@1 records with this client).",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "consent_id",
+ "purpose",
+ "granted_at",
+ "text_version"
+ ],
+ "properties": {
+ "consent_id": {
+ "type": "string",
+ "pattern": "^cns_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "purpose": {
+ "enum": [
+ "ai.train",
+ "data.public",
+ "face.recognize",
+ "app.data"
+ ]
+ },
+ "granted_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "text_version": {
+ "type": "string",
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$"
+ }
+ }
+ }
+ },
+ "quiet": {
+ "type": "array",
+ "maxItems": 4,
+ "description": "The purposes the person refused this app in the last 30 days: the app does not ask for them again until quiet_until. DELETE /api/v1/me/consent/:purpose/:client/refusal ends that early, so the next sign-in asks.",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "purpose",
+ "refused_at",
+ "quiet_until"
+ ],
+ "properties": {
+ "purpose": {
+ "enum": [
+ "ai.train",
+ "data.public",
+ "face.recognize",
+ "app.data"
+ ]
+ },
+ "refused_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "quiet_until": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"network.consent-check-request": {
"network.consent-check-request": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
@@ -93110,7 +93328,7 @@
@@ -93110,7 +93328,7 @@
"client": {
"client": {
"type": "string",
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
- "description": "app.data only: the app asking. Required for app.data."
+ "description": "The app the use is for. Required for app.data. For any other purpose it asks about that app's own grant from its sign-in (consent:<purpose>, 0.310.0); without it the answer is about OpenVibe's own use. One app's grant never answers for OpenVibe or for another app."
},
},
"feature": {
"feature": {
"enum": [
"enum": [
@@ -93143,7 +93361,7 @@
@@ -93143,7 +93361,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/network/consent-check-result.v1.json",
"$id": "https://openvibe.network/contracts/network/consent-check-result.v1.json",
"title": "NetworkConsentCheckResult",
"title": "NetworkConsentCheckResult",
- "description": "network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.",
+ "description": "network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An app's grant (client set) answers only a check that names that app. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
"required": [
"required": [
@@ -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.Actor API",
"title": "OpenVibe.Actor API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.AI API",
"title": "OpenVibe.AI API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Bench API",
"title": "OpenVibe.Bench API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Bot API",
"title": "OpenVibe.Bot API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Domains API",
"title": "OpenVibe.Domains API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Food API",
"title": "OpenVibe.Food API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.310.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"
@@ -145,7 +145,7 @@
@@ -145,7 +145,7 @@
"service": "network",
"service": "network",
"name": "OpenVibe.Network",
"name": "OpenVibe.Network",
"origin": "https://openvibe.network",
"origin": "https://openvibe.network",
- "operations": 60,
+ "operations": 62,
"capabilities": 35
"capabilities": 35
},
},
{
{
@@ -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.Inventory API",
"title": "OpenVibe.Inventory API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Live API",
"title": "OpenVibe.Live API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.MediaHub API",
"title": "OpenVibe.MediaHub API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.310.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"
@@ -658,7 +658,7 @@
@@ -658,7 +658,7 @@
"get": {
"get": {
"operationId": "get_api_v1_me_consent",
"operationId": "get_api_v1_me_consent",
"summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
"summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client revokes one app. Off is the default for every purpose. Only the person's own session or token reaches it.",
+ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
"tags": [
"tags": [
"network.consent"
"network.consent"
],
],
@@ -790,7 +790,7 @@
@@ -790,7 +790,7 @@
"put": {
"put": {
"operationId": "put_api_v1_me_consent_purpose",
"operationId": "put_api_v1_me_consent_purpose",
"summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
"summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client revokes one app. Off is the default for every purpose. Only the person's own session or token reaches it.",
+ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
"tags": [
"tags": [
"network.consent"
"network.consent"
],
],
@@ -856,7 +856,7 @@
@@ -856,7 +856,7 @@
"delete": {
"delete": {
"operationId": "delete_api_v1_me_consent_purpose_client",
"operationId": "delete_api_v1_me_consent_purpose_client",
"summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
"summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client revokes one app. Off is the default for every purpose. Only the person's own session or token reaches it.",
+ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
"tags": [
"tags": [
"network.consent"
"network.consent"
],
],
@@ -1000,6 +1000,286 @@
@@ -1000,6 +1000,286 @@
}
}
}
}
},
},
+ "/api/v1/me/consent/{purpose}/{client}/refusal": {
+ "delete": {
+ "operationId": "delete_api_v1_me_consent_purpose_client_refusal",
+ "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
+ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
+ "tags": [
+ "network.consent"
+ ],
+ "x-openvibe-capabilities": [
+ "network.consent.manage"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "network.consent.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/network.consent-choice.v1"
+ },
+ "parameters": [
+ {
+ "name": "purpose",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "client",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "on",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "name": "kinds",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 8,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "streams",
+ "vods",
+ "clips",
+ "voice",
+ "chat",
+ "posts",
+ "pictures",
+ "code"
+ ]
+ }
+ }
+ },
+ {
+ "name": "options",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "licence": {
+ "enum": [
+ "cc-by-4.0",
+ "cc0-1.0"
+ ]
+ },
+ "features": {
+ "type": "array",
+ "maxItems": 4,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "verify",
+ "find",
+ "cast",
+ "library"
+ ]
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "text_version",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",
+ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
+ },
+ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
+ },
+ {
+ "name": "adult",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
+ },
+ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/network.me-consent-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/me/consent/apps": {
+ "get": {
+ "operationId": "get_api_v1_me_consent_apps",
+ "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
+ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
+ "tags": [
+ "network.consent"
+ ],
+ "x-openvibe-capabilities": [
+ "network.consent.manage"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "network.consent.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/network.consent-choice.v1"
+ },
+ "parameters": [
+ {
+ "name": "on",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "name": "kinds",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 8,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "streams",
+ "vods",
+ "clips",
+ "voice",
+ "chat",
+ "posts",
+ "pictures",
+ "code"
+ ]
+ }
+ }
+ },
+ {
+ "name": "options",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "licence": {
+ "enum": [
+ "cc-by-4.0",
+ "cc0-1.0"
+ ]
+ },
+ "features": {
+ "type": "array",
+ "maxItems": 4,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "verify",
+ "find",
+ "cast",
+ "library"
+ ]
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "text_version",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",
+ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
+ },
+ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
+ },
+ {
+ "name": "adult",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
+ },
+ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/network.me-consent-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/v1/node/self/capabilities": {
"/api/v1/node/self/capabilities": {
"put": {
"put": {
"operationId": "put_api_v1_node_self_capabilities",
"operationId": "put_api_v1_node_self_capabilities",
@@ -4735,7 +5015,7 @@
@@ -4735,7 +5015,7 @@
"client": {
"client": {
"type": "string",
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
- "description": "app.data only: the app asking. Required for app.data."
+ "description": "The app the use is for. Required for app.data. For any other purpose it asks about that app's own grant from its sign-in (consent:<purpose>, 0.310.0); without it the answer is about OpenVibe's own use. One app's grant never answers for OpenVibe or for another app."
},
},
"feature": {
"feature": {
"enum": [
"enum": [
@@ -4761,7 +5041,7 @@
@@ -4761,7 +5041,7 @@
},
},
"network.consent-check-result.v1": {
"network.consent-check-result.v1": {
"title": "NetworkConsentCheckResult",
"title": "NetworkConsentCheckResult",
- "description": "network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.",
+ "description": "network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An app's grant (client set) answers only a check that names that app. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
"required": [
"required": [
@@ -4967,7 +5247,7 @@
@@ -4967,7 +5247,7 @@
"null"
"null"
],
],
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
- "description": "The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of an app the person granted (required for app.data, and only there)."
+ "description": "The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of the app the person granted it to. app.data always names one. Since 0.310.0 an app may ask for any purpose at sign-in (consent:<purpose>, network.consent-claim@1): the grant is one record per purpose with this client and source oauth, and it allows that app only, never OpenVibe itself or another app."
},
},
"options": {
"options": {
"type": "object",
"type": "object",
@@ -5006,7 +5286,7 @@
@@ -5006,7 +5286,7 @@
"ai.looks"
"ai.looks"
]
]
},
},
- "description": "app.data only: what the app may do with the person's content, as the consent screen showed it."
+ "description": "app.data only, and not written since 0.310.0: an app's consent screen grants each purpose as its own record (client set), so a reader asks for the purpose itself. Kept so older records still validate."
}
}
},
},
"description": "What the purpose needs beyond on and kinds. An empty object for ai.train and ai.looks."
"description": "What the purpose needs beyond on and kinds. An empty object for ai.train and ai.looks."
@@ -5023,7 +5303,7 @@
@@ -5023,7 +5303,7 @@
"api",
"api",
"import:vision"
"import:vision"
],
],
- "description": "Where the choice was made: the account page, an app's consent screen, the person's own API call, or OpenVibe.Vision's older face consents moved into Network."
+ "description": "Where the choice was made: the account page, an app's consent screen at sign-in (consent:<purpose>, with client set), the person's own API call, or OpenVibe.Vision's older face consents moved into Network."
},
},
"granted_at": {
"granted_at": {
"type": "string",
"type": "string",
@@ -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.309.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.310.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": "OpenRestream API",
"title": "OpenRestream API",
- "version": "0.309.0",
- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Play API",
"title": "OpenVibe.Play API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Quest API",
"title": "OpenVibe.Quest API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Report API",
"title": "OpenVibe.Report API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Run API",
"title": "OpenVibe.Run API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Services API",
"title": "OpenVibe.Services API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Space API",
"title": "OpenVibe.Space API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Today API",
"title": "OpenVibe.Today API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Vision API",
"title": "OpenVibe.Vision API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Watch API",
"title": "OpenVibe.Watch API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.309.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Work API",
"title": "OpenVibe.Work API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.310.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.Zone API",
"title": "OpenVibe.Zone API",
- "version": "0.309.0",
- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.309.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.310.0",
+ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.310.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"
@@ -95,6 +95,29 @@ export type ServiceTokenClaims = {
@@ -95,6 +95,29 @@ export type ServiceTokenClaims = {
* The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Also present on a node token that asks for an audience where the machine acts for its owner (Agent Streams S1: openvibe.live and live.agent_stream.publish); absent on every other client_credentials token.
* The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Also present on a node token that asks for an audience where the machine acts for its owner (Agent Streams S1: openvibe.live and live.agent_stream.publish); absent on every other client_credentials token.
*/
*/
on_behalf_of?: string;
on_behalf_of?: string;
+ /**
+ * App tokens from the authorization-code flow (0.310.0): the purposes the person (on_behalf_of) granted this app at sign-in and that are still active when the token is issued, the claim network.consent-claim@1 defines. Present when the sign-in asked for consent:<purpose> scopes, [] when none is granted, absent otherwise.
+ *
+ * @maxItems 4
+ */
+ consent?:
+ | []
+ | ["ai.train" | "data.public" | "face.recognize" | "app.data"]
+ | [
+ "ai.train" | "data.public" | "face.recognize" | "app.data",
+ "ai.train" | "data.public" | "face.recognize" | "app.data"
+ ]
+ | [
+ "ai.train" | "data.public" | "face.recognize" | "app.data",
+ "ai.train" | "data.public" | "face.recognize" | "app.data",
+ "ai.train" | "data.public" | "face.recognize" | "app.data"
+ ]
+ | [
+ "ai.train" | "data.public" | "face.recognize" | "app.data",
+ "ai.train" | "data.public" | "face.recognize" | "app.data",
+ "ai.train" | "data.public" | "face.recognize" | "app.data",
+ "ai.train" | "data.public" | "face.recognize" | "app.data"
+ ];
/**
/**
* Agent tokens: the host that runs the agent and the token was minted for (RFC 8693 actor claim), a service or an app.
* Agent tokens: the host that runs the agent and the token was minted for (RFC 8693 actor claim), a service or an app.
*/
*/
@@ -272002,7 +272025,7 @@ export interface NetworkConsent {
@@ -272002,7 +272025,7 @@ export interface NetworkConsent {
"streams" | "vods" | "clips" | "voice" | "chat" | "posts" | "pictures" | "code"
"streams" | "vods" | "clips" | "voice" | "chat" | "posts" | "pictures" | "code"
];
];
/**
/**
- * The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of an app the person granted (required for app.data, and only there).
+ * The app the choice is for: null means OpenVibe itself; otherwise the OAuth client id of the app the person granted it to. app.data always names one. Since 0.310.0 an app may ask for any purpose at sign-in (consent:<purpose>, network.consent-claim@1): the grant is one record per purpose with this client and source oauth, and it allows that app only, never OpenVibe itself or another app.
*/
*/
client: string | null;
client: string | null;
/**
/**
@@ -272034,7 +272057,7 @@ export interface NetworkConsent {
@@ -272034,7 +272057,7 @@ export interface NetworkConsent {
"verify" | "find" | "cast" | "library"
"verify" | "find" | "cast" | "library"
];
];
/**
/**
- * app.data only: what the app may do with the person's content, as the consent screen showed it.
+ * app.data only, and not written since 0.310.0: an app's consent screen grants each purpose as its own record (client set), so a reader asks for the purpose itself. Kept so older records still validate.
*
*
* @maxItems 4
* @maxItems 4
*/
*/
@@ -272062,7 +272085,7 @@ export interface NetworkConsent {
@@ -272062,7 +272085,7 @@ export interface NetworkConsent {
*/
*/
text_version: string;
text_version: string;
/**
/**
- * Where the choice was made: the account page, an app's consent screen, the person's own API call, or OpenVibe.Vision's older face consents moved into Network.
+ * Where the choice was made: the account page, an app's consent screen at sign-in (consent:<purpose>, with client set), the person's own API call, or OpenVibe.Vision's older face consents moved into Network.
*/
*/
source: "account" | "oauth" | "api" | "import:vision";
source: "account" | "oauth" | "api" | "import:vision";
/**
/**
@@ -282846,6 +282869,838 @@ export interface NetworkMeConsentResult {
@@ -282846,6 +282869,838 @@ export interface NetworkMeConsentResult {
}[];
}[];
}
}
+/** [email protected] (owner: network) */
+/**
+ * network.consent-claim@1 (the consent plan's C4, 2026-11-07): the purposes a person granted to one app at sign-in, as the `consent` claim of the tokens OpenVibe.Network issues to that app. An app asks with one scope `consent:<purpose>` per purpose, beside its other scopes, in /oauth/authorize. The purposes an app may ask for are network.consent@1's except the retired ai.looks: ai.train, data.public, face.recognize and app.data. Any other `consent:` scope is refused with invalid_scope. The consent screen lists each purpose in plain words, off until the person ticks it, and the person can sign in with every purpose refused. A granted purpose is a network.consent@1 record with the app's OAuth client id as client and source oauth; the person turns it off on their account page (DELETE /api/v1/me/consent/:purpose/:client). A refused purpose is not asked again by that app for 30 days, unless the person asks for it again on their account page. The schema is the claims object of any token that carries the claim: a first-party user access token, a Dock token, or an app token (identity.service-token-claims@1).
+ */
+export interface NetworkConsentClaim {
+ /**
+ * Exactly the purposes this sign-in asked for that the person has granted to this app and that are active when the token is issued, in network.consent@1's order. Never a refused or unasked purpose and never another app's grant. An empty list means none is granted: the app learns "not granted", never whether the person refused or was not asked. The claim is absent when the sign-in asked for no consent scope. Every new token works it out again, so a purpose the person turns off is gone from the next one (a refresh, or the next code); a token already issued keeps it until it expires. A service about to use a person's content for an app still asks POST /internal/consent/check with the app as client. The token response's `scope` lists consent:<purpose> only for the purposes granted.
+ *
+ * @maxItems 4
+ */
+ consent:
+ | []
+ | ["ai.train" | "data.public" | "face.recognize" | "app.data"]
+ | [
+ "ai.train" | "data.public" | "face.recognize" | "app.data",
+ "ai.train" | "data.public" | "face.recognize" | "app.data"
+ ]
+ | [
+ "ai.train" | "data.public" | "face.recognize" | "app.data",
+ "ai.train" | "data.public" | "face.recognize" | "app.data",
+ "ai.train" | "data.public" | "face.recognize" | "app.data"
+ ]
+ | [
+ "ai.train" | "data.public" | "face.recognize" | "app.data",
+ "ai.train" | "data.public" | "face.recognize" | "app.data",
+ "ai.train" | "data.public" | "face.recognize" | "app.data",
+ "ai.train" | "data.public" | "face.recognize" | "app.data"
+ ];
+ [k: string]: unknown | undefined;
+}
+
+/** [email protected] (owner: network) */
+/**
+ * network.me-consent-apps-result@1 (the consent plan's C4, 2026-11-07): "Apps you allowed" on the signed-in person's account page. GET /api/v1/me/consent/apps on OpenVibe.Network. It lists every app the person granted a purpose to on its sign-in screen (consent:<purpose>, network.consent-claim@1), with the purposes granted and those refused in the last 30 days. It also carries the words the app consent screen shows for each purpose. Only the person's own session reads it; an app never sees this list, its own entry included.
+ */
+export interface NetworkMeConsentAppsResult {
+ /**
+ * The purposes an app may ask for, with the exact words the app consent screen shows.
+ *
+ * @maxItems 8
+ */
+ purposes:
+ | []
+ | [
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ }
+ ]
+ | [
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ }
+ ]
+ | [
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ }
+ ]
+ | [
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ }
+ ]
+ | [
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ }
+ ]
+ | [
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ }
+ ]
+ | [
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ }
+ ]
+ | [
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ },
+ {
+ id: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ title: string;
+ /**
+ * What ticking it allows the app, in one plain line.
+ */
+ allows: string;
+ /**
+ * What turning it off later cannot undo (empty when nothing).
+ */
+ cannot_undo: string;
+ /**
+ * Ticking it asks the person to confirm they are 18 or older.
+ */
+ adults_only: boolean;
+ text_version: string;
+ }
+ ];
+ /**
+ * One entry per app with a purpose granted or refused, apps with an active grant first, then by name.
+ *
+ * @maxItems 200
+ */
+ apps: {
+ /**
+ * The app's OAuth client id.
+ */
+ client: string;
+ /**
+ * The app's name as its developer (or OpenVibe, for its own sites) registered it.
+ */
+ name: string;
+ /**
+ * True for a developer's app (app_<ULID>), false for an OpenVibe site.
+ */
+ third_party: boolean;
+ /**
+ * The purposes the app holds now (active network.consent@1 records with this client).
+ *
+ * @maxItems 4
+ */
+ granted:
+ | []
+ | [
+ {
+ consent_id: string;
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ granted_at: string;
+ text_version: string;
+ }
+ ]
+ | [
+ {
+ consent_id: string;
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ granted_at: string;
+ text_version: string;
+ },
+ {
+ consent_id: string;
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ granted_at: string;
+ text_version: string;
+ }
+ ]
+ | [
+ {
+ consent_id: string;
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ granted_at: string;
+ text_version: string;
+ },
+ {
+ consent_id: string;
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ granted_at: string;
+ text_version: string;
+ },
+ {
+ consent_id: string;
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ granted_at: string;
+ text_version: string;
+ }
+ ]
+ | [
+ {
+ consent_id: string;
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ granted_at: string;
+ text_version: string;
+ },
+ {
+ consent_id: string;
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ granted_at: string;
+ text_version: string;
+ },
+ {
+ consent_id: string;
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ granted_at: string;
+ text_version: string;
+ },
+ {
+ consent_id: string;
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ granted_at: string;
+ text_version: string;
+ }
+ ];
+ /**
+ * The purposes the person refused this app in the last 30 days: the app does not ask for them again until quiet_until. DELETE /api/v1/me/consent/:purpose/:client/refusal ends that early, so the next sign-in asks.
+ *
+ * @maxItems 4
+ */
+ quiet:
+ | []
+ | [
+ {
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ refused_at: string;
+ quiet_until: string;
+ }
+ ]
+ | [
+ {
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ refused_at: string;
+ quiet_until: string;
+ },
+ {
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ refused_at: string;
+ quiet_until: string;
+ }
+ ]
+ | [
+ {
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ refused_at: string;
+ quiet_until: string;
+ },
+ {
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ refused_at: string;
+ quiet_until: string;
+ },
+ {
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ refused_at: string;
+ quiet_until: string;
+ }
+ ]
+ | [
+ {
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ refused_at: string;
+ quiet_until: string;
+ },
+ {
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ refused_at: string;
+ quiet_until: string;
+ },
+ {
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ refused_at: string;
+ quiet_until: string;
+ },
+ {
+ purpose: "ai.train" | "data.public" | "face.recognize" | "app.data";
+ refused_at: string;
+ quiet_until: string;
+ }
+ ];
+ }[];
+}
+
/** [email protected] (owner: network) */
/** [email protected] (owner: network) */
/**
/**
* network.consent-check-request@1 (L24): may this purpose be used for these people? POST /internal/consent/check on OpenVibe.Network with a service token holding network.consent.read. One purpose per call, up to 500 subjects (a dataset build or a chat log checks its authors in batches).
* network.consent-check-request@1 (L24): may this purpose be used for these people? POST /internal/consent/check on OpenVibe.Network with a service token holding network.consent.read. One purpose per call, up to 500 subjects (a dataset build or a chat log checks its authors in batches).
@@ -282860,7 +283715,7 @@ export interface NetworkConsentCheckRequest {
@@ -282860,7 +283715,7 @@ export interface NetworkConsentCheckRequest {
*/
*/
kind?: "streams" | "vods" | "clips" | "voice" | "chat" | "posts" | "pictures" | "code";
kind?: "streams" | "vods" | "clips" | "voice" | "chat" | "posts" | "pictures" | "code";
/**
/**
- * app.data only: the app asking. Required for app.data.
+ * The app the use is for. Required for app.data. For any other purpose it asks about that app's own grant from its sign-in (consent:<purpose>, 0.310.0); without it the answer is about OpenVibe's own use. One app's grant never answers for OpenVibe or for another app.
*/
*/
client?: string;
client?: string;
/**
/**
@@ -282878,7 +283733,7 @@ export interface NetworkConsentCheckRequest {
@@ -282878,7 +283733,7 @@ export interface NetworkConsentCheckRequest {
/** [email protected] (owner: network) */
/** [email protected] (owner: network) */
/**
/**
- * network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.
+ * network.consent-check-result@1 (L24): the answer to network.consent-check-request@1. Every subject asked about is in allowed: true only for an active choice that covers the purpose, the kind, the app and the feature asked about. An app's grant (client set) answers only a check that names that app. An unknown or deleted subject is false, the same as a person who never chose (the answer never says whether an account exists). A service caches an answer for at most 60 s and drops it on network.consent.changed; when Network cannot answer, every subject is false.
*/
*/
export interface NetworkConsentCheckResult {
export interface NetworkConsentCheckResult {
purpose: "ai.train" | "data.public" | "face.recognize" | "ai.looks" | "app.data";
purpose: "ai.train" | "data.public" | "face.recognize" | "ai.looks" | "app.data";
@@ -4,7 +4,7 @@
@@ -4,7 +4,7 @@
"owner": "network",
"owner": "network",
"status": "active",
"status": "active",
"visibility": "public",
"visibility": "public",
- "description": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client revokes one app. Off is the default for every purpose. Only the person's own session or token reaches it.",
+ "description": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
"inputSchema": "network.consent-choice@1",
"inputSchema": "network.consent-choice@1",
"outputSchema": "network.me-consent-result@1",
"outputSchema": "network.me-consent-result@1",
"permissions": [
"permissions": [
@@ -21,6 +21,8 @@
@@ -21,6 +21,8 @@
"implementedBy": [
"implementedBy": [
"GET /api/v1/me/consent",
"GET /api/v1/me/consent",
"PUT /api/v1/me/consent/:purpose",
"PUT /api/v1/me/consent/:purpose",
- "DELETE /api/v1/me/consent/:purpose/:client"
+ "DELETE /api/v1/me/consent/:purpose/:client",
+ "GET /api/v1/me/consent/apps",
+ "DELETE /api/v1/me/consent/:purpose/:client/refusal"
]
]
}
}
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.309.0",
+ "version": "0.310.0",
"lockfileVersion": 3,
"lockfileVersion": 3,
"requires": true,
"requires": true,
"packages": {
"packages": {
"": {
"": {
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.309.0",
+ "version": "0.310.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.309.0",
+ "version": "0.310.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",
@@ -57,6 +57,44 @@ for (const id of ['network.consent', 'network.consent-choice', 'network.me-conse
@@ -57,6 +57,44 @@ for (const id of ['network.consent', 'network.consent-choice', 'network.me-conse
}
}
}
}
+// Apps ask per purpose at sign-in (C4, 0.310.0): consent:<purpose> scopes and the `consent` claim. An app may ask for
+// every purpose of the family except the retired ai.looks, and the claim, the app token's claim and the account page's
+// "Apps you allowed" all list that same set.
+const APP_PURPOSES = PURPOSES.filter((p) => p !== 'ai.looks');
+const claim = schema('network/consent-claim.v1.json');
+const tokenClaims = schema('identity/service-token-claims.v1.json');
+const apps = schema('network/me-consent-apps-result.v1.json');
+for (const [name, list] of [
+ ['claim.consent', claim.properties.consent.items.enum],
+ ['service-token.consent', tokenClaims.properties.consent.items.enum],
+ ['apps.purposes.id', apps.properties.purposes.items.properties.id.enum],
+ ['apps.granted.purpose', apps.properties.apps.items.properties.granted.items.properties.purpose.enum],
+ ['apps.quiet.purpose', apps.properties.apps.items.properties.quiet.items.properties.purpose.enum],
+]) assert.deepStrictEqual(list, APP_PURPOSES, `${name} lists the purposes an app may ask for`);
+assert.strictEqual(claim.properties.consent.uniqueItems, true, 'a purpose is in the claim once');
+assert.strictEqual(claim.properties.consent.maxItems, APP_PURPOSES.length);
+// The claim carries what was granted, nothing about what was refused.
+assert.deepStrictEqual(Object.keys(claim.properties), ['consent'], 'the claim names granted purposes only');
+assert.ok(!('refused' in apps.properties.apps.items.properties), 'the account page calls a refusal quiet, with its end');
+assert.strictEqual(apps.properties.apps.items.additionalProperties, false);
+// An app's grant is a consent record like any other: client set, source oauth.
+const appGrant = fx('network.consent', 'valid', 'oauth-app-train.json');
+assert.strictEqual(appGrant.source, 'oauth');
+assert.ok(appGrant.client && appGrant.purpose !== 'app.data', 'an app holds a purpose other than app.data under its own client id');
+// The account page reaches the new routes through the same capability.
+const manage = JSON.parse(fs.readFileSync(path.join(ROOT, 'manifests', 'capabilities', 'network.consent.manage.json'), 'utf8'));
+for (const route of ['GET /api/v1/me/consent/apps', 'DELETE /api/v1/me/consent/:purpose/:client/refusal']) {
+ assert.ok(manage.implementedBy.includes(route), `network.consent.manage names ${route}`);
+}
+for (const id of ['network.consent-claim', 'network.me-consent-apps-result']) {
+ for (const kind of ['valid', 'invalid']) {
+ for (const f of fs.readdirSync(path.join(ROOT, 'fixtures', id, kind))) {
+ const r = c.validate(`${id}@1`, fx(id, kind, f));
+ assert.strictEqual(r.valid, kind === 'valid', `${id} ${kind}/${f}: ${JSON.stringify(r.errors)}`);
+ }
+ }
+}
+
// The change event names the change, never the content it covers.
// The change event names the change, never the content it covers.
assert.strictEqual(changed.additionalProperties, false);
assert.strictEqual(changed.additionalProperties, false);
assert.ok(!('content' in changed.properties) && !('text' in changed.properties), 'the event carries no content');
assert.ok(!('content' in changed.properties) && !('text' in changed.properties), 'the event carries no content');
@@ -74,4 +112,4 @@ const network = JSON.parse(fs.readFileSync(path.join(ROOT, 'manifests', 'service
@@ -74,4 +112,4 @@ const network = JSON.parse(fs.readFileSync(path.join(ROOT, 'manifests', 'service
assert.ok(network.capabilities.includes('network.consent.read') && network.capabilities.includes('network.consent.manage'));
assert.ok(network.capabilities.includes('network.consent.read') && network.capabilities.includes('network.consent.manage'));
assert.ok(network.eventsProduced.includes('network.consent.changed'));
assert.ok(network.eventsProduced.includes('network.consent.changed'));
-console.log('network.consent@1: one list of purposes and kinds, cns_ ids, no selling purpose, fixtures, capabilities');
+console.log('network.consent@1: one list of purposes and kinds, cns_ ids, no selling purpose, the consent claim and Apps you allowed, fixtures, capabilities');