openvibers/openvibe.contracts Commits
Contracts 0.176.0: person-owned resources can be controlled (common.owned-resource-control-request@1; bot, chat, openre, community resource.control) (#134)
Changes
@@@ -4,6 +4,23 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) thfrom `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,the notes were in the tag and commit messages (`git tag -n1`).++## 0.176.0 — 2026-10-15++++- **Person-owned resources can be controlled** (ADR-048 amendment, step 8 phase 2, the control half).++ `common.owned-resource-control-request@1` names a robot, room, restream definition or paste by its index kind and id++ and its owner (a person). It goes to the same `POST /api/v1/resources/control`.++ `contracts.resources.checkControlRequest` takes either form (`checkOwnedControlRequest` for this one) and refuses a++ person acting on someone else's resource.++- **Four control capabilities, active:**++ - `bot.resource.control`: stop and resume a robot's e-stop;++ - `chat.resource.control`: archive a room;++ - `openre.resource.control`: suspend and resume a restream definition;++ - `community.resource.control`: delete a paste.++++ Each is listed in its service's manifest.++- **Fixtures:** valid `bot-robot-stop` and `community-paste-delete-dry-run`; invalid `with-project`, `agent-owner` and++ `create`.++## 0.175.0 — 2026-10-15- **`media.usage.recorded`** (ADR-031 section 8): Media's per-zone hourly rollups (`common.usage-recorded@1`). Each one@@@ -6445,6 +6445,16 @@"status": "active","adr": "ADR-048"},++ {++ "id": "common.owned-resource-control-request",++ "version": "1.0.0",++ "owner": "contracts",++ "schema": "common/owned-resource-control-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active",++ "adr": "ADR-048"++ },{"id": "common.resource-control-result","version": "1.0.0",nnew file mode 100644@@@ -0,0 +1,93 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/common/owned-resource-control-request.v1.json",++ "title": "OwnedResourceControlRequest",++ "description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "action",++ "owned",++ "owner",++ "idempotency_key"++ ],++ "properties": {++ "action": {++ "enum": [++ "delete",++ "stop",++ "suspend",++ "resume",++ "archive"++ ]++ },++ "owned": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "kind",++ "id"++ ],++ "description": "The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).",++ "properties": {++ "kind": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{0,31}\\.[a-z][a-z0-9-]{0,39}$"++ },++ "id": {++ "type": "string",++ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",++ "description": "The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room)."++ }++ }++ },++ "owner": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "type",++ "id"++ ],++ "description": "The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.",++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ }++ },++ "params": {++ "type": "object",++ "description": "Action-specific input, shaped by the authority's own contract (never redefined here)."++ },++ "idempotency_key": {++ "type": "string",++ "minLength": 8,++ "maxLength": 200++ },++ "on_behalf_of": {++ "$ref": "../identity/subject-ref.v1.json",++ "description": "Who asks: the owner, or their agent (whose sensitive actions wait for the owner's approval)."++ },++ "confirmation_id": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 80,++ "description": "The approved confirmation from an earlier confirmation_required answer."++ },++ "dry_run": {++ "type": "boolean",++ "default": false,++ "description": "Decide and explain without applying."++ },++ "trace_id": {++ "type": "string",++ "pattern": "^[0-9a-f]{32}$"++ }++ }++}@@@ -6,7 +6,8 @@id prefixes Events and Codes still owe, plus the prefixes not yet in `lib/ids.js`. Amended 2026-10-07 (step 8, theContracts half): only Network lists projects, Media lists objects alone, Events waits for a stored queue row, Codeshosts no repositories, the person-owned kinds are step 8 phase 2, and the chosen prefixes joined `lib/ids.js`. No--contract schema changes.++contract schema changes. Amended 2026-10-15 (step 8 phase 2, the control half): person-owned resources are controlled++through `common.owned-resource-control-request@1` (see "Person-owned resources" below).Builds on ADR-034 §2 (one resource name) and §5 (control planeand data plane) and on ADR-046 §6 (the data plane keeps running without the control plane). Contracts:`common.resource-name@1`, `common.resource-control-request@1`, `common.resource-control-result@1`; helpers in@@@ -131,6 +132,27 @@ every missing prefix, resolves the `run_` collision and adds them to `lib/ids.js`confirmation_required` appears only on `refused`, and `done` and `pending` carry no problem.`contracts.resources.checkControlResult` enforces these rules.++## Person-owned resources (amendment 2026-10-15)++++Robots (Bot), chat rooms (Chat), restream definitions (OpenRestream) and pastes (Community) belong to a person. They++are in no project and have no OVRN, so the project-scoped request cannot name them. They are controlled with++`common.owned-resource-control-request@1`, sent to the same `POST /api/v1/resources/control` route.++++- **Naming:** a request names the resource by its index summary's `kind` and `id` (`owned`) and its `owner` (a++ `usr_` subject). It carries no `project_id`. The authority tells the two forms apart by `owned`.++- **Who decides:** the authority refuses unless its own record has the same owner. Services offers the actions only++ on the viewer's own resources ("Your resources"), and sends `owner` and `on_behalf_of` as the viewer.++ `contracts.resources.checkControlRequest` checks either form and refuses a person acting on someone else's resource.++- **Gates:** an agent's sensitive actions (deleting, physical control such as a robot's stop) wait for the owner's++ approval, as with project resources. Idempotency is per (caller, key).++- **Answers:** `common.resource-control-result@1` without `resource`.++- **Actions per authority**, as each `<service>.resource.control` capability lists them; any other is refused++ `unsupported`:++ - `bot.robot`: stop (the remote e-stop) and resume (clear it);++ - `chat.room`: archive;++ - `openre.stream`: suspend and resume (the definition disabled or active);++ - `community.paste`: delete.++## Out of scope- The console surfaces, the Services repository and its deploy (T13 steps 3, 9, 15).nnew file mode 100644@@@ -0,0 +1,12 @@++{++ "action": "stop",++ "owned": {++ "kind": "bot.robot",++ "id": "rob_01JAB2C3D4E5F6G7H8J9K0MNPR"++ },++ "owner": {++ "type": "agent",++ "id": "agt_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "idempotency_key": "idem_stop_robot_2"++}nnew file mode 100644@@@ -0,0 +1,12 @@++{++ "action": "create",++ "owned": {++ "kind": "chat.room",++ "id": "new"++ },++ "owner": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "idempotency_key": "idem_create_room"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "action": "archive",++ "owned": {++ "kind": "chat.room",++ "id": "lobby"++ },++ "owner": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "idempotency_key": "idem_archive_1"++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "action": "stop",++ "owned": {++ "kind": "bot.robot",++ "id": "rob_01JAB2C3D4E5F6G7H8J9K0MNPR"++ },++ "owner": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "on_behalf_of": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "idempotency_key": "idem_stop_robot_1"++}nnew file mode 100644@@@ -0,0 +1,14 @@++{++ "action": "delete",++ "owned": {++ "kind": "community.paste",++ "id": "my-notes-2026"++ },++ "owner": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "idempotency_key": "idem_delete_paste_1",++ "dry_run": true,++ "trace_id": "0af7651916cd43dd8448eb211c80319c"++}@@@ -74823,6 +74823,104 @@}}},++ "common.owned-resource-control-request": {++ "version": "1.0.0",++ "owner": "contracts",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/common/owned-resource-control-request.v1.json",++ "title": "OwnedResourceControlRequest",++ "description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "action",++ "owned",++ "owner",++ "idempotency_key"++ ],++ "properties": {++ "action": {++ "enum": [++ "delete",++ "stop",++ "suspend",++ "resume",++ "archive"++ ]++ },++ "owned": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "kind",++ "id"++ ],++ "description": "The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).",++ "properties": {++ "kind": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{0,31}\\.[a-z][a-z0-9-]{0,39}$"++ },++ "id": {++ "type": "string",++ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",++ "description": "The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room)."++ }++ }++ },++ "owner": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "type",++ "id"++ ],++ "description": "The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.",++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ }++ },++ "params": {++ "type": "object",++ "description": "Action-specific input, shaped by the authority's own contract (never redefined here)."++ },++ "idempotency_key": {++ "type": "string",++ "minLength": 8,++ "maxLength": 200++ },++ "on_behalf_of": {++ "$ref": "../identity/subject-ref.v1.json",++ "description": "Who asks: the owner, or their agent (whose sensitive actions wait for the owner's approval)."++ },++ "confirmation_id": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 80,++ "description": "The approved confirmation from an earlier confirmation_required answer."++ },++ "dry_run": {++ "type": "boolean",++ "default": false,++ "description": "Decide and explain without applying."++ },++ "trace_id": {++ "type": "string",++ "pattern": "^[0-9a-f]{32}$"++ }++ }++ }++ },"common.resource-control-result": {"version": "1.0.0","owner": "contracts",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Actor API",-- "version": "0.175.0",-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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"@@@ -574,6 +574,62 @@}}},++ "/api/v1/resources/control": {++ "post": {++ "operationId": "post_api_v1_resources_control",++ "summary": "Control a person's robots through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a ",++ "description": "**bot.resource.control** (first-party): Control a person's robots through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a bot.robot (id rob_<ULID>) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: stop (latch the robot's remote e-stop, as POST /api/v1/robots/:id/estop: every actuator stops and nothing moves until it is cleared) and resume (clear that e-stop, as POST /api/v1/robots/:id/estop/clear; a local stop on the robot itself still holds); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",++ "tags": [++ "bot.resource"++ ],++ "x-openvibe-capabilities": [++ "bot.resource.control"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "bot.resource.control"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.owned-resource-control-request.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.resource-control-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/api/v1/robots": {"get": {"operationId": "get_api_v1_robots",@@@ -2365,6 +2421,153 @@}}},++ "common.owned-resource-control-request.v1": {++ "title": "OwnedResourceControlRequest",++ "description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "action",++ "owned",++ "owner",++ "idempotency_key"++ ],++ "properties": {++ "action": {++ "enum": [++ "delete",++ "stop",++ "suspend",++ "resume",++ "archive"++ ]++ },++ "owned": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "kind",++ "id"++ ],++ "description": "The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).",++ "properties": {++ "kind": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{0,31}\\.[a-z][a-z0-9-]{0,39}$"++ },++ "id": {++ "type": "string",++ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",++ "description": "The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room)."++ }++ }++ },++ "owner": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "type",++ "id"++ ],++ "description": "The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.",++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ }++ },++ "params": {++ "type": "object",++ "description": "Action-specific input, shaped by the authority's own contract (never redefined here)."++ },++ "idempotency_key": {++ "type": "string",++ "minLength": 8,++ "maxLength": 200++ },++ "on_behalf_of": {++ "$ref": "#/components/schemas/identity.subject-ref.v1",++ "description": "Who asks: the owner, or their agent (whose sensitive actions wait for the owner's approval)."++ },++ "confirmation_id": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 80,++ "description": "The approved confirmation from an earlier confirmation_required answer."++ },++ "dry_run": {++ "type": "boolean",++ "default": false,++ "description": "Decide and explain without applying."++ },++ "trace_id": {++ "type": "string",++ "pattern": "^[0-9a-f]{32}$"++ }++ }++ },++ "common.resource-control-result.v1": {++ "title": "ResourceControlResult",++ "description": "common.resource-control-result@1: the authority's answer to a common.resource-control-request@1 (ADR-048, plan T13). state is the outcome, not the resource's own state: done (applied), pending (accepted and asynchronous; the authority reports completion later on its events), refused (a policy or confirmation gate said no) or failed (an error). State rules: refused carries problem or confirmation_required; failed carries problem; done and pending carry neither; confirmation_required appears only on refused. result carries the authority's own response document and never another service's rows. The state rules are checked by contracts.resources.checkControlResult.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "action",++ "state",++ "at"++ ],++ "properties": {++ "action": {++ "type": "string"++ },++ "resource": {++ "$ref": "#/components/schemas/common.resource-name.v1"++ },++ "state": {++ "enum": [++ "done",++ "pending",++ "refused",++ "failed"++ ]++ },++ "result": {++ "type": "object"++ },++ "confirmation_required": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "confirmation_id",++ "reason"++ ],++ "properties": {++ "confirmation_id": {++ "type": "string",++ "maxLength": 80++ },++ "reason": {++ "type": "string"++ }++ },++ "description": "The action needs its owner's approval; retry the same request with this confirmation_id once approved."++ },++ "problem": {++ "$ref": "#/components/schemas/errors.problem.v1",++ "description": "RFC 9457 body when refused or failed."++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ },"common.resource-list-result.v1": {"title": "ResourceListResult","description": "A page of the resource index (roadmap WS-Z7).",@@@ -2411,6 +2614,16 @@}}},++ "common.resource-name.v1": {++ "title": "ResourceName",++ "description": "common.resource-name@1: the one stable, cross-service name of an OpenVibe resource (OVRN; ADR-034 section 2, ADR-048, plan T13): ovrn:<service>:<project_id>:<type>/<id>, for example ovrn:media:prj_01J0000000000000000000000A:object/med_01K0000000000000000000000B. The service segment is the authority that owns the resource; the project segment is its tenancy boundary; type is the second half of a common.resource-summary@1 kind and id is that summary's id, so the name composes from any resource summary. It is used in grant scopes, audit rows, event subjects, usage records, bill lines and console URLs. It never names a person, an app, a request, a session or an event. The pattern is the one common.usage-recorded@1 resource and common.resource-summary@1 ovrn already use.",++ "type": "string",++ "maxLength": 200,++ "pattern": "^ovrn:[a-z][a-z0-9-]{0,31}:prj_[0-9A-HJKMNP-TV-Z]{26}:[a-z][a-z0-9-]{0,39}/[a-z]{3}_[0-9A-HJKMNP-TV-Z]{26}$",++ "not": {++ "pattern": "/(usr|gst|app|mod|svc|job|evt|req)_"++ }++ },"common.resource-summary.v1": {"title": "ResourceSummary","description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Chat API",-- "version": "0.175.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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"@@@ -118,6 +118,62 @@}}},++ "/api/v1/resources/control": {++ "post": {++ "operationId": "post_api_v1_resources_control",++ "summary": "Control a person's rooms through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a c",++ "description": "**chat.resource.control** (first-party): Control a person's rooms through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a chat.room (id the room's slug) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: archive (the room leaves every listing and takes no new messages; its history is kept for its members, as an archived room); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",++ "tags": [++ "chat.resource"++ ],++ "x-openvibe-capabilities": [++ "chat.resource.control"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "chat.resource.control"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.owned-resource-control-request.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.resource-control-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/internal/chat/dm/block-state": {"get": {"operationId": "get_internal_chat_dm_block_state",@@@ -4845,6 +4901,153 @@}]},++ "common.owned-resource-control-request.v1": {++ "title": "OwnedResourceControlRequest",++ "description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "action",++ "owned",++ "owner",++ "idempotency_key"++ ],++ "properties": {++ "action": {++ "enum": [++ "delete",++ "stop",++ "suspend",++ "resume",++ "archive"++ ]++ },++ "owned": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "kind",++ "id"++ ],++ "description": "The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).",++ "properties": {++ "kind": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{0,31}\\.[a-z][a-z0-9-]{0,39}$"++ },++ "id": {++ "type": "string",++ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",++ "description": "The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room)."++ }++ }++ },++ "owner": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "type",++ "id"++ ],++ "description": "The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.",++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ }++ },++ "params": {++ "type": "object",++ "description": "Action-specific input, shaped by the authority's own contract (never redefined here)."++ },++ "idempotency_key": {++ "type": "string",++ "minLength": 8,++ "maxLength": 200++ },++ "on_behalf_of": {++ "$ref": "#/components/schemas/identity.subject-ref.v1",++ "description": "Who asks: the owner, or their agent (whose sensitive actions wait for the owner's approval)."++ },++ "confirmation_id": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 80,++ "description": "The approved confirmation from an earlier confirmation_required answer."++ },++ "dry_run": {++ "type": "boolean",++ "default": false,++ "description": "Decide and explain without applying."++ },++ "trace_id": {++ "type": "string",++ "pattern": "^[0-9a-f]{32}$"++ }++ }++ },++ "common.resource-control-result.v1": {++ "title": "ResourceControlResult",++ "description": "common.resource-control-result@1: the authority's answer to a common.resource-control-request@1 (ADR-048, plan T13). state is the outcome, not the resource's own state: done (applied), pending (accepted and asynchronous; the authority reports completion later on its events), refused (a policy or confirmation gate said no) or failed (an error). State rules: refused carries problem or confirmation_required; failed carries problem; done and pending carry neither; confirmation_required appears only on refused. result carries the authority's own response document and never another service's rows. The state rules are checked by contracts.resources.checkControlResult.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "action",++ "state",++ "at"++ ],++ "properties": {++ "action": {++ "type": "string"++ },++ "resource": {++ "$ref": "#/components/schemas/common.resource-name.v1"++ },++ "state": {++ "enum": [++ "done",++ "pending",++ "refused",++ "failed"++ ]++ },++ "result": {++ "type": "object"++ },++ "confirmation_required": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "confirmation_id",++ "reason"++ ],++ "properties": {++ "confirmation_id": {++ "type": "string",++ "maxLength": 80++ },++ "reason": {++ "type": "string"++ }++ },++ "description": "The action needs its owner's approval; retry the same request with this confirmation_id once approved."++ },++ "problem": {++ "$ref": "#/components/schemas/errors.problem.v1",++ "description": "RFC 9457 body when refused or failed."++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ },"common.resource-list-result.v1": {"title": "ResourceListResult","description": "A page of the resource index (roadmap WS-Z7).",@@@ -4891,6 +5094,16 @@}}},++ "common.resource-name.v1": {++ "title": "ResourceName",++ "description": "common.resource-name@1: the one stable, cross-service name of an OpenVibe resource (OVRN; ADR-034 section 2, ADR-048, plan T13): ovrn:<service>:<project_id>:<type>/<id>, for example ovrn:media:prj_01J0000000000000000000000A:object/med_01K0000000000000000000000B. The service segment is the authority that owns the resource; the project segment is its tenancy boundary; type is the second half of a common.resource-summary@1 kind and id is that summary's id, so the name composes from any resource summary. It is used in grant scopes, audit rows, event subjects, usage records, bill lines and console URLs. It never names a person, an app, a request, a session or an event. The pattern is the one common.usage-recorded@1 resource and common.resource-summary@1 ovrn already use.",++ "type": "string",++ "maxLength": 200,++ "pattern": "^ovrn:[a-z][a-z0-9-]{0,31}:prj_[0-9A-HJKMNP-TV-Z]{26}:[a-z][a-z0-9-]{0,39}/[a-z]{3}_[0-9A-HJKMNP-TV-Z]{26}$",++ "not": {++ "pattern": "/(usr|gst|app|mod|svc|job|evt|req)_"++ }++ },"common.resource-summary.v1": {"title": "ResourceSummary","description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Codes API",-- "version": "0.175.0",-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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"@@@ -1454,6 +1454,62 @@}}},++ "/api/v1/resources/control": {++ "post": {++ "operationId": "post_api_v1_resources_control",++ "summary": "Control a person's pastes through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a ",++ "description": "**community.resource.control** (first-party): Control a person's pastes through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a community.paste (id the paste's slug) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: delete (the paste goes through Community's own deletion path, as the owner deleting it on openvibe.community); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",++ "tags": [++ "community.resource"++ ],++ "x-openvibe-capabilities": [++ "community.resource.control"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "community.resource.control"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.owned-resource-control-request.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.resource-control-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/api/v1/spaces": {"get": {"operationId": "get_api_v1_spaces",@@@ -2456,6 +2512,153 @@"type": "object","maxProperties": 0},++ "common.owned-resource-control-request.v1": {++ "title": "OwnedResourceControlRequest",++ "description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "action",++ "owned",++ "owner",++ "idempotency_key"++ ],++ "properties": {++ "action": {++ "enum": [++ "delete",++ "stop",++ "suspend",++ "resume",++ "archive"++ ]++ },++ "owned": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "kind",++ "id"++ ],++ "description": "The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).",++ "properties": {++ "kind": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{0,31}\\.[a-z][a-z0-9-]{0,39}$"++ },++ "id": {++ "type": "string",++ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",++ "description": "The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room)."++ }++ }++ },++ "owner": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "type",++ "id"++ ],++ "description": "The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.",++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ }++ },++ "params": {++ "type": "object",++ "description": "Action-specific input, shaped by the authority's own contract (never redefined here)."++ },++ "idempotency_key": {++ "type": "string",++ "minLength": 8,++ "maxLength": 200++ },++ "on_behalf_of": {++ "$ref": "#/components/schemas/identity.subject-ref.v1",++ "description": "Who asks: the owner, or their agent (whose sensitive actions wait for the owner's approval)."++ },++ "confirmation_id": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 80,++ "description": "The approved confirmation from an earlier confirmation_required answer."++ },++ "dry_run": {++ "type": "boolean",++ "default": false,++ "description": "Decide and explain without applying."++ },++ "trace_id": {++ "type": "string",++ "pattern": "^[0-9a-f]{32}$"++ }++ }++ },++ "common.resource-control-result.v1": {++ "title": "ResourceControlResult",++ "description": "common.resource-control-result@1: the authority's answer to a common.resource-control-request@1 (ADR-048, plan T13). state is the outcome, not the resource's own state: done (applied), pending (accepted and asynchronous; the authority reports completion later on its events), refused (a policy or confirmation gate said no) or failed (an error). State rules: refused carries problem or confirmation_required; failed carries problem; done and pending carry neither; confirmation_required appears only on refused. result carries the authority's own response document and never another service's rows. The state rules are checked by contracts.resources.checkControlResult.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "action",++ "state",++ "at"++ ],++ "properties": {++ "action": {++ "type": "string"++ },++ "resource": {++ "$ref": "#/components/schemas/common.resource-name.v1"++ },++ "state": {++ "enum": [++ "done",++ "pending",++ "refused",++ "failed"++ ]++ },++ "result": {++ "type": "object"++ },++ "confirmation_required": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "confirmation_id",++ "reason"++ ],++ "properties": {++ "confirmation_id": {++ "type": "string",++ "maxLength": 80++ },++ "reason": {++ "type": "string"++ }++ },++ "description": "The action needs its owner's approval; retry the same request with this confirmation_id once approved."++ },++ "problem": {++ "$ref": "#/components/schemas/errors.problem.v1",++ "description": "RFC 9457 body when refused or failed."++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ },"common.resource-list-result.v1": {"title": "ResourceListResult","description": "A page of the resource index (roadmap WS-Z7).",@@@ -2502,6 +2705,16 @@}}},++ "common.resource-name.v1": {++ "title": "ResourceName",++ "description": "common.resource-name@1: the one stable, cross-service name of an OpenVibe resource (OVRN; ADR-034 section 2, ADR-048, plan T13): ovrn:<service>:<project_id>:<type>/<id>, for example ovrn:media:prj_01J0000000000000000000000A:object/med_01K0000000000000000000000B. The service segment is the authority that owns the resource; the project segment is its tenancy boundary; type is the second half of a common.resource-summary@1 kind and id is that summary's id, so the name composes from any resource summary. It is used in grant scopes, audit rows, event subjects, usage records, bill lines and console URLs. It never names a person, an app, a request, a session or an event. The pattern is the one common.usage-recorded@1 resource and common.resource-summary@1 ovrn already use.",++ "type": "string",++ "maxLength": 200,++ "pattern": "^ovrn:[a-z][a-z0-9-]{0,31}:prj_[0-9A-HJKMNP-TV-Z]{26}:[a-z][a-z0-9-]{0,39}/[a-z]{3}_[0-9A-HJKMNP-TV-Z]{26}$",++ "not": {++ "pattern": "/(usr|gst|app|mod|svc|job|evt|req)_"++ }++ },"common.resource-summary.v1": {"title": "ResourceSummary","description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Coupons API",-- "version": "0.175.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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"@@@ -33,15 +33,15 @@"service": "bot","name": "OpenVibe.Bot","origin": "https://openvibe.bot",-- "operations": 20,-- "capabilities": 6++ "operations": 21,++ "capabilities": 7},{"service": "chat","name": "OpenVibe.Chat","origin": "https://openvibe.chat",-- "operations": 24,-- "capabilities": 13++ "operations": 25,++ "capabilities": 14},{"service": "codes",@@@ -54,8 +54,8 @@"service": "community","name": "OpenVibe.Community","origin": "https://openvibe.community",-- "operations": 39,-- "capabilities": 12++ "operations": 40,++ "capabilities": 13},{"service": "coupons",@@@ -145,8 +145,8 @@"service": "openre","name": "OpenRestream","origin": "https://openre.stream",-- "operations": 24,-- "capabilities": 8++ "operations": 25,++ "capabilities": 9},{"service": "play",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Inventory API",-- "version": "0.175.0",-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.News API",-- "version": "0.175.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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"@@@ -551,6 +551,62 @@}}},++ "/api/v1/resources/control": {++ "post": {++ "operationId": "post_api_v1_resources_control",++ "summary": "Control a person's restream definitions through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-reque",++ "description": "**openre.resource.control** (first-party): Control a person's restream definitions through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a openre.stream (id the definition id) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: suspend (state disabled: new ingest is refused, as PATCH state=disabled) and resume (state active again); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",++ "tags": [++ "openre.resource"++ ],++ "x-openvibe-capabilities": [++ "openre.resource.control"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "openre.resource.control"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.owned-resource-control-request.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.resource-control-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/api/v1/sessions": {"get": {"operationId": "get_api_v1_sessions",@@@ -1395,6 +1451,153 @@"type": "object","maxProperties": 0},++ "common.owned-resource-control-request.v1": {++ "title": "OwnedResourceControlRequest",++ "description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "action",++ "owned",++ "owner",++ "idempotency_key"++ ],++ "properties": {++ "action": {++ "enum": [++ "delete",++ "stop",++ "suspend",++ "resume",++ "archive"++ ]++ },++ "owned": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "kind",++ "id"++ ],++ "description": "The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).",++ "properties": {++ "kind": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{0,31}\\.[a-z][a-z0-9-]{0,39}$"++ },++ "id": {++ "type": "string",++ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",++ "description": "The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room)."++ }++ }++ },++ "owner": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "type",++ "id"++ ],++ "description": "The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.",++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ }++ },++ "params": {++ "type": "object",++ "description": "Action-specific input, shaped by the authority's own contract (never redefined here)."++ },++ "idempotency_key": {++ "type": "string",++ "minLength": 8,++ "maxLength": 200++ },++ "on_behalf_of": {++ "$ref": "#/components/schemas/identity.subject-ref.v1",++ "description": "Who asks: the owner, or their agent (whose sensitive actions wait for the owner's approval)."++ },++ "confirmation_id": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 80,++ "description": "The approved confirmation from an earlier confirmation_required answer."++ },++ "dry_run": {++ "type": "boolean",++ "default": false,++ "description": "Decide and explain without applying."++ },++ "trace_id": {++ "type": "string",++ "pattern": "^[0-9a-f]{32}$"++ }++ }++ },++ "common.resource-control-result.v1": {++ "title": "ResourceControlResult",++ "description": "common.resource-control-result@1: the authority's answer to a common.resource-control-request@1 (ADR-048, plan T13). state is the outcome, not the resource's own state: done (applied), pending (accepted and asynchronous; the authority reports completion later on its events), refused (a policy or confirmation gate said no) or failed (an error). State rules: refused carries problem or confirmation_required; failed carries problem; done and pending carry neither; confirmation_required appears only on refused. result carries the authority's own response document and never another service's rows. The state rules are checked by contracts.resources.checkControlResult.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "action",++ "state",++ "at"++ ],++ "properties": {++ "action": {++ "type": "string"++ },++ "resource": {++ "$ref": "#/components/schemas/common.resource-name.v1"++ },++ "state": {++ "enum": [++ "done",++ "pending",++ "refused",++ "failed"++ ]++ },++ "result": {++ "type": "object"++ },++ "confirmation_required": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "confirmation_id",++ "reason"++ ],++ "properties": {++ "confirmation_id": {++ "type": "string",++ "maxLength": 80++ },++ "reason": {++ "type": "string"++ }++ },++ "description": "The action needs its owner's approval; retry the same request with this confirmation_id once approved."++ },++ "problem": {++ "$ref": "#/components/schemas/errors.problem.v1",++ "description": "RFC 9457 body when refused or failed."++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ },"common.resource-list-result.v1": {"title": "ResourceListResult","description": "A page of the resource index (roadmap WS-Z7).",@@@ -1441,6 +1644,16 @@}}},++ "common.resource-name.v1": {++ "title": "ResourceName",++ "description": "common.resource-name@1: the one stable, cross-service name of an OpenVibe resource (OVRN; ADR-034 section 2, ADR-048, plan T13): ovrn:<service>:<project_id>:<type>/<id>, for example ovrn:media:prj_01J0000000000000000000000A:object/med_01K0000000000000000000000B. The service segment is the authority that owns the resource; the project segment is its tenancy boundary; type is the second half of a common.resource-summary@1 kind and id is that summary's id, so the name composes from any resource summary. It is used in grant scopes, audit rows, event subjects, usage records, bill lines and console URLs. It never names a person, an app, a request, a session or an event. The pattern is the one common.usage-recorded@1 resource and common.resource-summary@1 ovrn already use.",++ "type": "string",++ "maxLength": 200,++ "pattern": "^ovrn:[a-z][a-z0-9-]{0,31}:prj_[0-9A-HJKMNP-TV-Z]{26}:[a-z][a-z0-9-]{0,39}/[a-z]{3}_[0-9A-HJKMNP-TV-Z]{26}$",++ "not": {++ "pattern": "/(usr|gst|app|mod|svc|job|evt|req)_"++ }++ },"common.resource-summary.v1": {"title": "ResourceSummary","description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Play API",-- "version": "0.175.0",-- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",-- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",++ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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"@@@ -178621,6 +178621,46 @@ export type ResourceControlRequest = {[k: string]: unknown | undefined;};++/** [email protected] (owner: contracts) */++/**++ * common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.++ */++export interface OwnedResourceControlRequest {++ action: "delete" | "stop" | "suspend" | "resume" | "archive";++ /**++ * The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).++ */++ owned: {++ kind: string;++ /**++ * The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room).++ */++ id: string;++ };++ /**++ * The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.++ */++ owner: {++ type: "user";++ id: string;++ };++ /**++ * Action-specific input, shaped by the authority's own contract (never redefined here).++ */++ params?: {};++ idempotency_key: string;++ on_behalf_of?: SubjectRef;++ /**++ * The approved confirmation from an earlier confirmation_required answer.++ */++ confirmation_id?: string | null;++ /**++ * Decide and explain without applying.++ */++ dry_run?: boolean;++ trace_id?: string;++}++/** [email protected] (owner: contracts) *//*** common.resource-control-result@1: the authority's answer to a common.resource-control-request@1 (ADR-048, plan T13). state is the outcome, not the resource's own state: done (applied), pending (accepted and asynchronous; the authority reports completion later on its events), refused (a policy or confirmation gate said no) or failed (an error). State rules: refused carries problem or confirmation_required; failed carries problem; done and pending carry neither; confirmation_required appears only on refused. result carries the authority's own response document and never another service's rows. The state rules are checked by contracts.resources.checkControlResult.@@@ -40,7 +40,13 @@ function nameOf(summary) {function checkName(name) { return registry.validate('common.resource-name@1', name); }++/**++ * A control request: common.resource-control-request@1 for a project's resource (named by its OVRN), or++ * common.owned-resource-control-request@1 for a person-owned one (named by `owned`, its index kind and id; ADR-048++ * amendment 2026-10-15). The two are told apart by `owned`.++ */function checkControlRequest(q) {++ if (q && typeof q === 'object' && q.owned !== undefined) return checkOwnedControlRequest(q);const r = registry.validate('common.resource-control-request@1', q);if (!r.valid) return r;const errors = [];@@@ -51,6 +57,18 @@ function checkControlRequest(q) {return { valid: errors.length === 0, errors };}++/** A person-owned resource's control request: valid, and its kind names the authority's service as a resource kind does. */++function checkOwnedControlRequest(q) {++ const r = registry.validate('common.owned-resource-control-request@1', q);++ if (!r.valid) return r;++ const errors = [];++ // An agent may ask for its owner; nobody else may ask for someone's resource.++ if (q.on_behalf_of && q.on_behalf_of.type === 'user' && q.on_behalf_of.id !== q.owner.id) {++ errors.push({ path: '/on_behalf_of', message: 'a person acts only on what they own' });++ }++ return { valid: errors.length === 0, errors };++}++function checkControlResult(res) {const r = registry.validate('common.resource-control-result@1', res);if (!r.valid) return r;@@@ -63,4 +81,4 @@ function checkControlResult(res) {return { valid: errors.length === 0, errors };}--module.exports = { parse, format, nameOf, checkName, checkControlRequest, checkControlResult };++module.exports = { parse, format, nameOf, checkName, checkControlRequest, checkOwnedControlRequest, checkControlResult };nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "id": "bot.resource.control",++ "version": "1.0",++ "owner": "bot",++ "status": "active",++ "visibility": "first-party",++ "sensitive": true,++ "description": "Control a person's robots through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a bot.robot (id rob_<ULID>) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: stop (latch the robot's remote e-stop, as POST /api/v1/robots/:id/estop: every actuator stops and nothing moves until it is cleared) and resume (clear that e-stop, as POST /api/v1/robots/:id/estop/clear; a local stop on the robot itself still holds); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",++ "permissions": [++ "resources:control"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "standard",++ "events": [],++ "implementedBy": [++ "POST /api/v1/resources/control"++ ],++ "inputSchema": "common.owned-resource-control-request@1",++ "outputSchema": "common.resource-control-result@1"++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "id": "chat.resource.control",++ "version": "1.0",++ "owner": "chat",++ "status": "active",++ "visibility": "first-party",++ "sensitive": true,++ "description": "Control a person's rooms through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a chat.room (id the room's slug) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: archive (the room leaves every listing and takes no new messages; its history is kept for its members, as an archived room); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",++ "permissions": [++ "resources:control"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "standard",++ "events": [],++ "implementedBy": [++ "POST /api/v1/resources/control"++ ],++ "inputSchema": "common.owned-resource-control-request@1",++ "outputSchema": "common.resource-control-result@1"++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "id": "community.resource.control",++ "version": "1.0",++ "owner": "community",++ "status": "active",++ "visibility": "first-party",++ "sensitive": true,++ "description": "Control a person's pastes through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a community.paste (id the paste's slug) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: delete (the paste goes through Community's own deletion path, as the owner deleting it on openvibe.community); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",++ "permissions": [++ "resources:control"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "standard",++ "events": [],++ "implementedBy": [++ "POST /api/v1/resources/control"++ ],++ "inputSchema": "common.owned-resource-control-request@1",++ "outputSchema": "common.resource-control-result@1"++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "id": "openre.resource.control",++ "version": "1.0",++ "owner": "openre",++ "status": "active",++ "visibility": "first-party",++ "sensitive": true,++ "description": "Control a person's restream definitions through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a openre.stream (id the definition id) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: suspend (state disabled: new ingest is refused, as PATCH state=disabled) and resume (state active again); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",++ "permissions": [++ "resources:control"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "standard",++ "events": [],++ "implementedBy": [++ "POST /api/v1/resources/control"++ ],++ "inputSchema": "common.owned-resource-control-request@1",++ "outputSchema": "common.resource-control-result@1"++}@@@ -33,7 +33,8 @@"bot.computer.control","bot.device.connect","bot.job.dispatch",-- "bot.resource.read"++ "bot.resource.read",++ "bot.resource.control"],"eventsProduced": ["bot.robot.online",@@@ -26,7 +26,8 @@"chat.sounds.write","chat.stats.read","chat.ticket.write",-- "chat.resource.read"++ "chat.resource.read",++ "chat.resource.control"],"eventsProduced": ["chat.message.created",@@@ -25,7 +25,8 @@"community.thread.read","community.vote.set","community.pulse.read",-- "community.resource.read"++ "community.resource.read",++ "community.resource.control"],"eventsProduced": ["community.comment.created",@@@ -125,7 +125,8 @@"openre.session.end","openre.output.read","openre.output.write",-- "openre.resource.read"++ "openre.resource.read",++ "openre.resource.control"],"eventsProduced": ["openre.session.started",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.175.0",++ "version": "0.176.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",@@@ -9,7 +9,8 @@ const c = require('..');// bot.resource.read: the robots in OpenVibe.Services' resource index (planned 0.129.0, active 0.130.0; plan T13 step 8).// bot.computer.control: an owner's agent uses their own machine (0.166.0, plan T14/T17; Bot's STATUS.json lists it with// the computer kinds).--const BOT_CAPABILITIES = ['bot.robot.read', 'bot.robot.manage', 'bot.robot.control', 'bot.computer.control', 'bot.device.connect', 'bot.job.dispatch', 'bot.resource.read'];++// bot.resource.control: an owner stops or resumes their robot from OpenVibe.Services (0.176.0, ADR-048 step 8 phase 2).++const BOT_CAPABILITIES = ['bot.robot.read', 'bot.robot.manage', 'bot.robot.control', 'bot.computer.control', 'bot.device.connect', 'bot.job.dispatch', 'bot.resource.read', 'bot.resource.control'];const BOT_EVENTS = ['bot.robot.online', 'bot.robot.offline', 'bot.estop.set', 'bot.estop.cleared', 'bot.command.refused'];const bot = c.services.get('bot');
@@ -4,6 +4,23 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
@@ -4,6 +4,23 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
the notes were in the tag and commit messages (`git tag -n1`).
+## 0.176.0 — 2026-10-15
+
+- **Person-owned resources can be controlled** (ADR-048 amendment, step 8 phase 2, the control half).
+ `common.owned-resource-control-request@1` names a robot, room, restream definition or paste by its index kind and id
+ and its owner (a person). It goes to the same `POST /api/v1/resources/control`.
+ `contracts.resources.checkControlRequest` takes either form (`checkOwnedControlRequest` for this one) and refuses a
+ person acting on someone else's resource.
+- **Four control capabilities, active:**
+ - `bot.resource.control`: stop and resume a robot's e-stop;
+ - `chat.resource.control`: archive a room;
+ - `openre.resource.control`: suspend and resume a restream definition;
+ - `community.resource.control`: delete a paste.
+
+ Each is listed in its service's manifest.
+- **Fixtures:** valid `bot-robot-stop` and `community-paste-delete-dry-run`; invalid `with-project`, `agent-owner` and
+ `create`.
+
## 0.175.0 — 2026-10-15
## 0.175.0 — 2026-10-15
- **`media.usage.recorded`** (ADR-031 section 8): Media's per-zone hourly rollups (`common.usage-recorded@1`). Each one
- **`media.usage.recorded`** (ADR-031 section 8): Media's per-zone hourly rollups (`common.usage-recorded@1`). Each one
@@ -6445,6 +6445,16 @@
@@ -6445,6 +6445,16 @@
"status": "active",
"status": "active",
"adr": "ADR-048"
"adr": "ADR-048"
},
},
+ {
+ "id": "common.owned-resource-control-request",
+ "version": "1.0.0",
+ "owner": "contracts",
+ "schema": "common/owned-resource-control-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active",
+ "adr": "ADR-048"
+ },
{
{
"id": "common.resource-control-result",
"id": "common.resource-control-result",
"version": "1.0.0",
"version": "1.0.0",
new file mode 100644
new file mode 100644
@@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/common/owned-resource-control-request.v1.json",
+ "title": "OwnedResourceControlRequest",
+ "description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "action",
+ "owned",
+ "owner",
+ "idempotency_key"
+ ],
+ "properties": {
+ "action": {
+ "enum": [
+ "delete",
+ "stop",
+ "suspend",
+ "resume",
+ "archive"
+ ]
+ },
+ "owned": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "kind",
+ "id"
+ ],
+ "description": "The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{0,31}\\.[a-z][a-z0-9-]{0,39}$"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",
+ "description": "The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room)."
+ }
+ }
+ },
+ "owner": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "id"
+ ],
+ "description": "The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.",
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ }
+ },
+ "params": {
+ "type": "object",
+ "description": "Action-specific input, shaped by the authority's own contract (never redefined here)."
+ },
+ "idempotency_key": {
+ "type": "string",
+ "minLength": 8,
+ "maxLength": 200
+ },
+ "on_behalf_of": {
+ "$ref": "../identity/subject-ref.v1.json",
+ "description": "Who asks: the owner, or their agent (whose sensitive actions wait for the owner's approval)."
+ },
+ "confirmation_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 80,
+ "description": "The approved confirmation from an earlier confirmation_required answer."
+ },
+ "dry_run": {
+ "type": "boolean",
+ "default": false,
+ "description": "Decide and explain without applying."
+ },
+ "trace_id": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{32}$"
+ }
+ }
+}
@@ -6,7 +6,8 @@
@@ -6,7 +6,8 @@
id prefixes Events and Codes still owe, plus the prefixes not yet in `lib/ids.js`. Amended 2026-10-07 (step 8, the
id prefixes Events and Codes still owe, plus the prefixes not yet in `lib/ids.js`. Amended 2026-10-07 (step 8, the
Contracts half): only Network lists projects, Media lists objects alone, Events waits for a stored queue row, Codes
Contracts half): only Network lists projects, Media lists objects alone, Events waits for a stored queue row, Codes
hosts no repositories, the person-owned kinds are step 8 phase 2, and the chosen prefixes joined `lib/ids.js`. No
hosts no repositories, the person-owned kinds are step 8 phase 2, and the chosen prefixes joined `lib/ids.js`. No
-contract schema changes.
+contract schema changes. Amended 2026-10-15 (step 8 phase 2, the control half): person-owned resources are controlled
+through `common.owned-resource-control-request@1` (see "Person-owned resources" below).
Builds on ADR-034 §2 (one resource name) and §5 (control plane
Builds on ADR-034 §2 (one resource name) and §5 (control plane
and data plane) and on ADR-046 §6 (the data plane keeps running without the control plane). Contracts:
and data plane) and on ADR-046 §6 (the data plane keeps running without the control plane). Contracts:
`common.resource-name@1`, `common.resource-control-request@1`, `common.resource-control-result@1`; helpers in
`common.resource-name@1`, `common.resource-control-request@1`, `common.resource-control-result@1`; helpers in
@@ -131,6 +132,27 @@ every missing prefix, resolves the `run_` collision and adds them to `lib/ids.js
@@ -131,6 +132,27 @@ every missing prefix, resolves the `run_` collision and adds them to `lib/ids.js
`confirmation_required` appears only on `refused`, and `done` and `pending` carry no problem.
`confirmation_required` appears only on `refused`, and `done` and `pending` carry no problem.
`contracts.resources.checkControlResult` enforces these rules.
`contracts.resources.checkControlResult` enforces these rules.
+## Person-owned resources (amendment 2026-10-15)
+
+Robots (Bot), chat rooms (Chat), restream definitions (OpenRestream) and pastes (Community) belong to a person. They
+are in no project and have no OVRN, so the project-scoped request cannot name them. They are controlled with
+`common.owned-resource-control-request@1`, sent to the same `POST /api/v1/resources/control` route.
+
+- **Naming:** a request names the resource by its index summary's `kind` and `id` (`owned`) and its `owner` (a
+ `usr_` subject). It carries no `project_id`. The authority tells the two forms apart by `owned`.
+- **Who decides:** the authority refuses unless its own record has the same owner. Services offers the actions only
+ on the viewer's own resources ("Your resources"), and sends `owner` and `on_behalf_of` as the viewer.
+ `contracts.resources.checkControlRequest` checks either form and refuses a person acting on someone else's resource.
+- **Gates:** an agent's sensitive actions (deleting, physical control such as a robot's stop) wait for the owner's
+ approval, as with project resources. Idempotency is per (caller, key).
+- **Answers:** `common.resource-control-result@1` without `resource`.
+- **Actions per authority**, as each `<service>.resource.control` capability lists them; any other is refused
+ `unsupported`:
+ - `bot.robot`: stop (the remote e-stop) and resume (clear it);
+ - `chat.room`: archive;
+ - `openre.stream`: suspend and resume (the definition disabled or active);
+ - `community.paste`: delete.
+
## Out of scope
## Out of scope
- The console surfaces, the Services repository and its deploy (T13 steps 3, 9, 15).
- The console surfaces, the Services repository and its deploy (T13 steps 3, 9, 15).
new file mode 100644
new file mode 100644
@@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
+{
+ "action": "stop",
+ "owned": {
+ "kind": "bot.robot",
+ "id": "rob_01JAB2C3D4E5F6G7H8J9K0MNPR"
+ },
+ "owner": {
+ "type": "agent",
+ "id": "agt_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "idempotency_key": "idem_stop_robot_2"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
+{
+ "action": "create",
+ "owned": {
+ "kind": "chat.room",
+ "id": "new"
+ },
+ "owner": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "idempotency_key": "idem_create_room"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "action": "archive",
+ "owned": {
+ "kind": "chat.room",
+ "id": "lobby"
+ },
+ "owner": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "idempotency_key": "idem_archive_1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "action": "stop",
+ "owned": {
+ "kind": "bot.robot",
+ "id": "rob_01JAB2C3D4E5F6G7H8J9K0MNPR"
+ },
+ "owner": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "on_behalf_of": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "idempotency_key": "idem_stop_robot_1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
+{
+ "action": "delete",
+ "owned": {
+ "kind": "community.paste",
+ "id": "my-notes-2026"
+ },
+ "owner": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "idempotency_key": "idem_delete_paste_1",
+ "dry_run": true,
+ "trace_id": "0af7651916cd43dd8448eb211c80319c"
+}
@@ -74823,6 +74823,104 @@
@@ -74823,6 +74823,104 @@
}
}
}
}
},
},
+ "common.owned-resource-control-request": {
+ "version": "1.0.0",
+ "owner": "contracts",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/common/owned-resource-control-request.v1.json",
+ "title": "OwnedResourceControlRequest",
+ "description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "action",
+ "owned",
+ "owner",
+ "idempotency_key"
+ ],
+ "properties": {
+ "action": {
+ "enum": [
+ "delete",
+ "stop",
+ "suspend",
+ "resume",
+ "archive"
+ ]
+ },
+ "owned": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "kind",
+ "id"
+ ],
+ "description": "The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{0,31}\\.[a-z][a-z0-9-]{0,39}$"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",
+ "description": "The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room)."
+ }
+ }
+ },
+ "owner": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "id"
+ ],
+ "description": "The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.",
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ }
+ },
+ "params": {
+ "type": "object",
+ "description": "Action-specific input, shaped by the authority's own contract (never redefined here)."
+ },
+ "idempotency_key": {
+ "type": "string",
+ "minLength": 8,
+ "maxLength": 200
+ },
+ "on_behalf_of": {
+ "$ref": "../identity/subject-ref.v1.json",
+ "description": "Who asks: the owner, or their agent (whose sensitive actions wait for the owner's approval)."
+ },
+ "confirmation_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 80,
+ "description": "The approved confirmation from an earlier confirmation_required answer."
+ },
+ "dry_run": {
+ "type": "boolean",
+ "default": false,
+ "description": "Decide and explain without applying."
+ },
+ "trace_id": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{32}$"
+ }
+ }
+ }
+ },
"common.resource-control-result": {
"common.resource-control-result": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "contracts",
"owner": "contracts",
@@ -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.175.0",
- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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"
@@ -574,6 +574,62 @@
@@ -574,6 +574,62 @@
}
}
}
}
},
},
+ "/api/v1/resources/control": {
+ "post": {
+ "operationId": "post_api_v1_resources_control",
+ "summary": "Control a person's robots through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a ",
+ "description": "**bot.resource.control** (first-party): Control a person's robots through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a bot.robot (id rob_<ULID>) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: stop (latch the robot's remote e-stop, as POST /api/v1/robots/:id/estop: every actuator stops and nothing moves until it is cleared) and resume (clear that e-stop, as POST /api/v1/robots/:id/estop/clear; a local stop on the robot itself still holds); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",
+ "tags": [
+ "bot.resource"
+ ],
+ "x-openvibe-capabilities": [
+ "bot.resource.control"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "bot.resource.control"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.owned-resource-control-request.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.resource-control-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/v1/robots": {
"/api/v1/robots": {
"get": {
"get": {
"operationId": "get_api_v1_robots",
"operationId": "get_api_v1_robots",
@@ -2365,6 +2421,153 @@
@@ -2365,6 +2421,153 @@
}
}
}
}
},
},
+ "common.owned-resource-control-request.v1": {
+ "title": "OwnedResourceControlRequest",
+ "description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "action",
+ "owned",
+ "owner",
+ "idempotency_key"
+ ],
+ "properties": {
+ "action": {
+ "enum": [
+ "delete",
+ "stop",
+ "suspend",
+ "resume",
+ "archive"
+ ]
+ },
+ "owned": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "kind",
+ "id"
+ ],
+ "description": "The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{0,31}\\.[a-z][a-z0-9-]{0,39}$"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",
+ "description": "The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room)."
+ }
+ }
+ },
+ "owner": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "id"
+ ],
+ "description": "The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.",
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ }
+ },
+ "params": {
+ "type": "object",
+ "description": "Action-specific input, shaped by the authority's own contract (never redefined here)."
+ },
+ "idempotency_key": {
+ "type": "string",
+ "minLength": 8,
+ "maxLength": 200
+ },
+ "on_behalf_of": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1",
+ "description": "Who asks: the owner, or their agent (whose sensitive actions wait for the owner's approval)."
+ },
+ "confirmation_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 80,
+ "description": "The approved confirmation from an earlier confirmation_required answer."
+ },
+ "dry_run": {
+ "type": "boolean",
+ "default": false,
+ "description": "Decide and explain without applying."
+ },
+ "trace_id": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{32}$"
+ }
+ }
+ },
+ "common.resource-control-result.v1": {
+ "title": "ResourceControlResult",
+ "description": "common.resource-control-result@1: the authority's answer to a common.resource-control-request@1 (ADR-048, plan T13). state is the outcome, not the resource's own state: done (applied), pending (accepted and asynchronous; the authority reports completion later on its events), refused (a policy or confirmation gate said no) or failed (an error). State rules: refused carries problem or confirmation_required; failed carries problem; done and pending carry neither; confirmation_required appears only on refused. result carries the authority's own response document and never another service's rows. The state rules are checked by contracts.resources.checkControlResult.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "action",
+ "state",
+ "at"
+ ],
+ "properties": {
+ "action": {
+ "type": "string"
+ },
+ "resource": {
+ "$ref": "#/components/schemas/common.resource-name.v1"
+ },
+ "state": {
+ "enum": [
+ "done",
+ "pending",
+ "refused",
+ "failed"
+ ]
+ },
+ "result": {
+ "type": "object"
+ },
+ "confirmation_required": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "confirmation_id",
+ "reason"
+ ],
+ "properties": {
+ "confirmation_id": {
+ "type": "string",
+ "maxLength": 80
+ },
+ "reason": {
+ "type": "string"
+ }
+ },
+ "description": "The action needs its owner's approval; retry the same request with this confirmation_id once approved."
+ },
+ "problem": {
+ "$ref": "#/components/schemas/errors.problem.v1",
+ "description": "RFC 9457 body when refused or failed."
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
"common.resource-list-result.v1": {
"common.resource-list-result.v1": {
"title": "ResourceListResult",
"title": "ResourceListResult",
"description": "A page of the resource index (roadmap WS-Z7).",
"description": "A page of the resource index (roadmap WS-Z7).",
@@ -2411,6 +2614,16 @@
@@ -2411,6 +2614,16 @@
}
}
}
}
},
},
+ "common.resource-name.v1": {
+ "title": "ResourceName",
+ "description": "common.resource-name@1: the one stable, cross-service name of an OpenVibe resource (OVRN; ADR-034 section 2, ADR-048, plan T13): ovrn:<service>:<project_id>:<type>/<id>, for example ovrn:media:prj_01J0000000000000000000000A:object/med_01K0000000000000000000000B. The service segment is the authority that owns the resource; the project segment is its tenancy boundary; type is the second half of a common.resource-summary@1 kind and id is that summary's id, so the name composes from any resource summary. It is used in grant scopes, audit rows, event subjects, usage records, bill lines and console URLs. It never names a person, an app, a request, a session or an event. The pattern is the one common.usage-recorded@1 resource and common.resource-summary@1 ovrn already use.",
+ "type": "string",
+ "maxLength": 200,
+ "pattern": "^ovrn:[a-z][a-z0-9-]{0,31}:prj_[0-9A-HJKMNP-TV-Z]{26}:[a-z][a-z0-9-]{0,39}/[a-z]{3}_[0-9A-HJKMNP-TV-Z]{26}$",
+ "not": {
+ "pattern": "/(usr|gst|app|mod|svc|job|evt|req)_"
+ }
+ },
"common.resource-summary.v1": {
"common.resource-summary.v1": {
"title": "ResourceSummary",
"title": "ResourceSummary",
"description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",
"description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",
@@ -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.175.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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"
@@ -118,6 +118,62 @@
@@ -118,6 +118,62 @@
}
}
}
}
},
},
+ "/api/v1/resources/control": {
+ "post": {
+ "operationId": "post_api_v1_resources_control",
+ "summary": "Control a person's rooms through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a c",
+ "description": "**chat.resource.control** (first-party): Control a person's rooms through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a chat.room (id the room's slug) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: archive (the room leaves every listing and takes no new messages; its history is kept for its members, as an archived room); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",
+ "tags": [
+ "chat.resource"
+ ],
+ "x-openvibe-capabilities": [
+ "chat.resource.control"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "chat.resource.control"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.owned-resource-control-request.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.resource-control-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/internal/chat/dm/block-state": {
"/internal/chat/dm/block-state": {
"get": {
"get": {
"operationId": "get_internal_chat_dm_block_state",
"operationId": "get_internal_chat_dm_block_state",
@@ -4845,6 +4901,153 @@
@@ -4845,6 +4901,153 @@
}
}
]
]
},
},
+ "common.owned-resource-control-request.v1": {
+ "title": "OwnedResourceControlRequest",
+ "description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "action",
+ "owned",
+ "owner",
+ "idempotency_key"
+ ],
+ "properties": {
+ "action": {
+ "enum": [
+ "delete",
+ "stop",
+ "suspend",
+ "resume",
+ "archive"
+ ]
+ },
+ "owned": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "kind",
+ "id"
+ ],
+ "description": "The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{0,31}\\.[a-z][a-z0-9-]{0,39}$"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",
+ "description": "The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room)."
+ }
+ }
+ },
+ "owner": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "id"
+ ],
+ "description": "The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.",
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ }
+ },
+ "params": {
+ "type": "object",
+ "description": "Action-specific input, shaped by the authority's own contract (never redefined here)."
+ },
+ "idempotency_key": {
+ "type": "string",
+ "minLength": 8,
+ "maxLength": 200
+ },
+ "on_behalf_of": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1",
+ "description": "Who asks: the owner, or their agent (whose sensitive actions wait for the owner's approval)."
+ },
+ "confirmation_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 80,
+ "description": "The approved confirmation from an earlier confirmation_required answer."
+ },
+ "dry_run": {
+ "type": "boolean",
+ "default": false,
+ "description": "Decide and explain without applying."
+ },
+ "trace_id": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{32}$"
+ }
+ }
+ },
+ "common.resource-control-result.v1": {
+ "title": "ResourceControlResult",
+ "description": "common.resource-control-result@1: the authority's answer to a common.resource-control-request@1 (ADR-048, plan T13). state is the outcome, not the resource's own state: done (applied), pending (accepted and asynchronous; the authority reports completion later on its events), refused (a policy or confirmation gate said no) or failed (an error). State rules: refused carries problem or confirmation_required; failed carries problem; done and pending carry neither; confirmation_required appears only on refused. result carries the authority's own response document and never another service's rows. The state rules are checked by contracts.resources.checkControlResult.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "action",
+ "state",
+ "at"
+ ],
+ "properties": {
+ "action": {
+ "type": "string"
+ },
+ "resource": {
+ "$ref": "#/components/schemas/common.resource-name.v1"
+ },
+ "state": {
+ "enum": [
+ "done",
+ "pending",
+ "refused",
+ "failed"
+ ]
+ },
+ "result": {
+ "type": "object"
+ },
+ "confirmation_required": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "confirmation_id",
+ "reason"
+ ],
+ "properties": {
+ "confirmation_id": {
+ "type": "string",
+ "maxLength": 80
+ },
+ "reason": {
+ "type": "string"
+ }
+ },
+ "description": "The action needs its owner's approval; retry the same request with this confirmation_id once approved."
+ },
+ "problem": {
+ "$ref": "#/components/schemas/errors.problem.v1",
+ "description": "RFC 9457 body when refused or failed."
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
"common.resource-list-result.v1": {
"common.resource-list-result.v1": {
"title": "ResourceListResult",
"title": "ResourceListResult",
"description": "A page of the resource index (roadmap WS-Z7).",
"description": "A page of the resource index (roadmap WS-Z7).",
@@ -4891,6 +5094,16 @@
@@ -4891,6 +5094,16 @@
}
}
}
}
},
},
+ "common.resource-name.v1": {
+ "title": "ResourceName",
+ "description": "common.resource-name@1: the one stable, cross-service name of an OpenVibe resource (OVRN; ADR-034 section 2, ADR-048, plan T13): ovrn:<service>:<project_id>:<type>/<id>, for example ovrn:media:prj_01J0000000000000000000000A:object/med_01K0000000000000000000000B. The service segment is the authority that owns the resource; the project segment is its tenancy boundary; type is the second half of a common.resource-summary@1 kind and id is that summary's id, so the name composes from any resource summary. It is used in grant scopes, audit rows, event subjects, usage records, bill lines and console URLs. It never names a person, an app, a request, a session or an event. The pattern is the one common.usage-recorded@1 resource and common.resource-summary@1 ovrn already use.",
+ "type": "string",
+ "maxLength": 200,
+ "pattern": "^ovrn:[a-z][a-z0-9-]{0,31}:prj_[0-9A-HJKMNP-TV-Z]{26}:[a-z][a-z0-9-]{0,39}/[a-z]{3}_[0-9A-HJKMNP-TV-Z]{26}$",
+ "not": {
+ "pattern": "/(usr|gst|app|mod|svc|job|evt|req)_"
+ }
+ },
"common.resource-summary.v1": {
"common.resource-summary.v1": {
"title": "ResourceSummary",
"title": "ResourceSummary",
"description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",
"description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",
@@ -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.175.0",
- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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"
@@ -1454,6 +1454,62 @@
@@ -1454,6 +1454,62 @@
}
}
}
}
},
},
+ "/api/v1/resources/control": {
+ "post": {
+ "operationId": "post_api_v1_resources_control",
+ "summary": "Control a person's pastes through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a ",
+ "description": "**community.resource.control** (first-party): Control a person's pastes through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a community.paste (id the paste's slug) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: delete (the paste goes through Community's own deletion path, as the owner deleting it on openvibe.community); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",
+ "tags": [
+ "community.resource"
+ ],
+ "x-openvibe-capabilities": [
+ "community.resource.control"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "community.resource.control"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.owned-resource-control-request.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.resource-control-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/v1/spaces": {
"/api/v1/spaces": {
"get": {
"get": {
"operationId": "get_api_v1_spaces",
"operationId": "get_api_v1_spaces",
@@ -2456,6 +2512,153 @@
@@ -2456,6 +2512,153 @@
"type": "object",
"type": "object",
"maxProperties": 0
"maxProperties": 0
},
},
+ "common.owned-resource-control-request.v1": {
+ "title": "OwnedResourceControlRequest",
+ "description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "action",
+ "owned",
+ "owner",
+ "idempotency_key"
+ ],
+ "properties": {
+ "action": {
+ "enum": [
+ "delete",
+ "stop",
+ "suspend",
+ "resume",
+ "archive"
+ ]
+ },
+ "owned": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "kind",
+ "id"
+ ],
+ "description": "The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{0,31}\\.[a-z][a-z0-9-]{0,39}$"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",
+ "description": "The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room)."
+ }
+ }
+ },
+ "owner": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "id"
+ ],
+ "description": "The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.",
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ }
+ },
+ "params": {
+ "type": "object",
+ "description": "Action-specific input, shaped by the authority's own contract (never redefined here)."
+ },
+ "idempotency_key": {
+ "type": "string",
+ "minLength": 8,
+ "maxLength": 200
+ },
+ "on_behalf_of": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1",
+ "description": "Who asks: the owner, or their agent (whose sensitive actions wait for the owner's approval)."
+ },
+ "confirmation_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 80,
+ "description": "The approved confirmation from an earlier confirmation_required answer."
+ },
+ "dry_run": {
+ "type": "boolean",
+ "default": false,
+ "description": "Decide and explain without applying."
+ },
+ "trace_id": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{32}$"
+ }
+ }
+ },
+ "common.resource-control-result.v1": {
+ "title": "ResourceControlResult",
+ "description": "common.resource-control-result@1: the authority's answer to a common.resource-control-request@1 (ADR-048, plan T13). state is the outcome, not the resource's own state: done (applied), pending (accepted and asynchronous; the authority reports completion later on its events), refused (a policy or confirmation gate said no) or failed (an error). State rules: refused carries problem or confirmation_required; failed carries problem; done and pending carry neither; confirmation_required appears only on refused. result carries the authority's own response document and never another service's rows. The state rules are checked by contracts.resources.checkControlResult.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "action",
+ "state",
+ "at"
+ ],
+ "properties": {
+ "action": {
+ "type": "string"
+ },
+ "resource": {
+ "$ref": "#/components/schemas/common.resource-name.v1"
+ },
+ "state": {
+ "enum": [
+ "done",
+ "pending",
+ "refused",
+ "failed"
+ ]
+ },
+ "result": {
+ "type": "object"
+ },
+ "confirmation_required": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "confirmation_id",
+ "reason"
+ ],
+ "properties": {
+ "confirmation_id": {
+ "type": "string",
+ "maxLength": 80
+ },
+ "reason": {
+ "type": "string"
+ }
+ },
+ "description": "The action needs its owner's approval; retry the same request with this confirmation_id once approved."
+ },
+ "problem": {
+ "$ref": "#/components/schemas/errors.problem.v1",
+ "description": "RFC 9457 body when refused or failed."
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
"common.resource-list-result.v1": {
"common.resource-list-result.v1": {
"title": "ResourceListResult",
"title": "ResourceListResult",
"description": "A page of the resource index (roadmap WS-Z7).",
"description": "A page of the resource index (roadmap WS-Z7).",
@@ -2502,6 +2705,16 @@
@@ -2502,6 +2705,16 @@
}
}
}
}
},
},
+ "common.resource-name.v1": {
+ "title": "ResourceName",
+ "description": "common.resource-name@1: the one stable, cross-service name of an OpenVibe resource (OVRN; ADR-034 section 2, ADR-048, plan T13): ovrn:<service>:<project_id>:<type>/<id>, for example ovrn:media:prj_01J0000000000000000000000A:object/med_01K0000000000000000000000B. The service segment is the authority that owns the resource; the project segment is its tenancy boundary; type is the second half of a common.resource-summary@1 kind and id is that summary's id, so the name composes from any resource summary. It is used in grant scopes, audit rows, event subjects, usage records, bill lines and console URLs. It never names a person, an app, a request, a session or an event. The pattern is the one common.usage-recorded@1 resource and common.resource-summary@1 ovrn already use.",
+ "type": "string",
+ "maxLength": 200,
+ "pattern": "^ovrn:[a-z][a-z0-9-]{0,31}:prj_[0-9A-HJKMNP-TV-Z]{26}:[a-z][a-z0-9-]{0,39}/[a-z]{3}_[0-9A-HJKMNP-TV-Z]{26}$",
+ "not": {
+ "pattern": "/(usr|gst|app|mod|svc|job|evt|req)_"
+ }
+ },
"common.resource-summary.v1": {
"common.resource-summary.v1": {
"title": "ResourceSummary",
"title": "ResourceSummary",
"description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",
"description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",
@@ -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.175.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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"
@@ -33,15 +33,15 @@
@@ -33,15 +33,15 @@
"service": "bot",
"service": "bot",
"name": "OpenVibe.Bot",
"name": "OpenVibe.Bot",
"origin": "https://openvibe.bot",
"origin": "https://openvibe.bot",
- "operations": 20,
- "capabilities": 6
+ "operations": 21,
+ "capabilities": 7
},
},
{
{
"service": "chat",
"service": "chat",
"name": "OpenVibe.Chat",
"name": "OpenVibe.Chat",
"origin": "https://openvibe.chat",
"origin": "https://openvibe.chat",
- "operations": 24,
- "capabilities": 13
+ "operations": 25,
+ "capabilities": 14
},
},
{
{
"service": "codes",
"service": "codes",
@@ -54,8 +54,8 @@
@@ -54,8 +54,8 @@
"service": "community",
"service": "community",
"name": "OpenVibe.Community",
"name": "OpenVibe.Community",
"origin": "https://openvibe.community",
"origin": "https://openvibe.community",
- "operations": 39,
- "capabilities": 12
+ "operations": 40,
+ "capabilities": 13
},
},
{
{
"service": "coupons",
"service": "coupons",
@@ -145,8 +145,8 @@
@@ -145,8 +145,8 @@
"service": "openre",
"service": "openre",
"name": "OpenRestream",
"name": "OpenRestream",
"origin": "https://openre.stream",
"origin": "https://openre.stream",
- "operations": 24,
- "capabilities": 8
+ "operations": 25,
+ "capabilities": 9
},
},
{
{
"service": "play",
"service": "play",
@@ -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.175.0",
- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.News API",
"title": "OpenVibe.News API",
- "version": "0.175.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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"
@@ -551,6 +551,62 @@
@@ -551,6 +551,62 @@
}
}
}
}
},
},
+ "/api/v1/resources/control": {
+ "post": {
+ "operationId": "post_api_v1_resources_control",
+ "summary": "Control a person's restream definitions through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-reque",
+ "description": "**openre.resource.control** (first-party): Control a person's restream definitions through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a openre.stream (id the definition id) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: suspend (state disabled: new ingest is refused, as PATCH state=disabled) and resume (state active again); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",
+ "tags": [
+ "openre.resource"
+ ],
+ "x-openvibe-capabilities": [
+ "openre.resource.control"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "openre.resource.control"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.owned-resource-control-request.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.resource-control-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/v1/sessions": {
"/api/v1/sessions": {
"get": {
"get": {
"operationId": "get_api_v1_sessions",
"operationId": "get_api_v1_sessions",
@@ -1395,6 +1451,153 @@
@@ -1395,6 +1451,153 @@
"type": "object",
"type": "object",
"maxProperties": 0
"maxProperties": 0
},
},
+ "common.owned-resource-control-request.v1": {
+ "title": "OwnedResourceControlRequest",
+ "description": "common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "action",
+ "owned",
+ "owner",
+ "idempotency_key"
+ ],
+ "properties": {
+ "action": {
+ "enum": [
+ "delete",
+ "stop",
+ "suspend",
+ "resume",
+ "archive"
+ ]
+ },
+ "owned": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "kind",
+ "id"
+ ],
+ "description": "The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{0,31}\\.[a-z][a-z0-9-]{0,39}$"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",
+ "description": "The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room)."
+ }
+ }
+ },
+ "owner": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "id"
+ ],
+ "description": "The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.",
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ }
+ },
+ "params": {
+ "type": "object",
+ "description": "Action-specific input, shaped by the authority's own contract (never redefined here)."
+ },
+ "idempotency_key": {
+ "type": "string",
+ "minLength": 8,
+ "maxLength": 200
+ },
+ "on_behalf_of": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1",
+ "description": "Who asks: the owner, or their agent (whose sensitive actions wait for the owner's approval)."
+ },
+ "confirmation_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 80,
+ "description": "The approved confirmation from an earlier confirmation_required answer."
+ },
+ "dry_run": {
+ "type": "boolean",
+ "default": false,
+ "description": "Decide and explain without applying."
+ },
+ "trace_id": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{32}$"
+ }
+ }
+ },
+ "common.resource-control-result.v1": {
+ "title": "ResourceControlResult",
+ "description": "common.resource-control-result@1: the authority's answer to a common.resource-control-request@1 (ADR-048, plan T13). state is the outcome, not the resource's own state: done (applied), pending (accepted and asynchronous; the authority reports completion later on its events), refused (a policy or confirmation gate said no) or failed (an error). State rules: refused carries problem or confirmation_required; failed carries problem; done and pending carry neither; confirmation_required appears only on refused. result carries the authority's own response document and never another service's rows. The state rules are checked by contracts.resources.checkControlResult.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "action",
+ "state",
+ "at"
+ ],
+ "properties": {
+ "action": {
+ "type": "string"
+ },
+ "resource": {
+ "$ref": "#/components/schemas/common.resource-name.v1"
+ },
+ "state": {
+ "enum": [
+ "done",
+ "pending",
+ "refused",
+ "failed"
+ ]
+ },
+ "result": {
+ "type": "object"
+ },
+ "confirmation_required": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "confirmation_id",
+ "reason"
+ ],
+ "properties": {
+ "confirmation_id": {
+ "type": "string",
+ "maxLength": 80
+ },
+ "reason": {
+ "type": "string"
+ }
+ },
+ "description": "The action needs its owner's approval; retry the same request with this confirmation_id once approved."
+ },
+ "problem": {
+ "$ref": "#/components/schemas/errors.problem.v1",
+ "description": "RFC 9457 body when refused or failed."
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
"common.resource-list-result.v1": {
"common.resource-list-result.v1": {
"title": "ResourceListResult",
"title": "ResourceListResult",
"description": "A page of the resource index (roadmap WS-Z7).",
"description": "A page of the resource index (roadmap WS-Z7).",
@@ -1441,6 +1644,16 @@
@@ -1441,6 +1644,16 @@
}
}
}
}
},
},
+ "common.resource-name.v1": {
+ "title": "ResourceName",
+ "description": "common.resource-name@1: the one stable, cross-service name of an OpenVibe resource (OVRN; ADR-034 section 2, ADR-048, plan T13): ovrn:<service>:<project_id>:<type>/<id>, for example ovrn:media:prj_01J0000000000000000000000A:object/med_01K0000000000000000000000B. The service segment is the authority that owns the resource; the project segment is its tenancy boundary; type is the second half of a common.resource-summary@1 kind and id is that summary's id, so the name composes from any resource summary. It is used in grant scopes, audit rows, event subjects, usage records, bill lines and console URLs. It never names a person, an app, a request, a session or an event. The pattern is the one common.usage-recorded@1 resource and common.resource-summary@1 ovrn already use.",
+ "type": "string",
+ "maxLength": 200,
+ "pattern": "^ovrn:[a-z][a-z0-9-]{0,31}:prj_[0-9A-HJKMNP-TV-Z]{26}:[a-z][a-z0-9-]{0,39}/[a-z]{3}_[0-9A-HJKMNP-TV-Z]{26}$",
+ "not": {
+ "pattern": "/(usr|gst|app|mod|svc|job|evt|req)_"
+ }
+ },
"common.resource-summary.v1": {
"common.resource-summary.v1": {
"title": "ResourceSummary",
"title": "ResourceSummary",
"description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",
"description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",
@@ -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.175.0",
- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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.175.0",
- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.175.0): each operation lists the capabilities it performs; a token for 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.176.0",
+ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.176.0): each operation lists the capabilities it performs; a token for 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"
@@ -178621,6 +178621,46 @@ export type ResourceControlRequest = {
@@ -178621,6 +178621,46 @@ export type ResourceControlRequest = {
[k: string]: unknown | undefined;
[k: string]: unknown | undefined;
};
};
+/** [email protected] (owner: contracts) */
+/**
+ * common.owned-resource-control-request@1 (ADR-048 amendment 2026-10-15, step 8 phase 2): one control operation on a person-owned resource. Robots, chat rooms, restream definitions and pastes belong to a person, not to a project, so they carry no OVRN and the project-scoped common.resource-control-request@1 cannot name them. OpenVibe.Services sends this to the owning authority's POST /api/v1/resources/control (the same route, told apart by `owned`), acting for the person in on_behalf_of. owned names the resource by its index summary's kind and id. owner is the person it belongs to: the authority refuses unless its own record says the same (Services also offers actions only on the viewer's own resources). A sensitive action (deleting, physical control) asked by an agent is refused with confirmation_required until the owner approves, as for project resources. idempotency_key makes a retry safe: the authority answers a repeated key from the same caller with the first result, and refuses the key reused with another request. Answered with common.resource-control-result@1 (without resource: there is no OVRN). The actions each authority takes are in its <service>.resource.control capability; any other action is refused unsupported.
+ */
+export interface OwnedResourceControlRequest {
+ action: "delete" | "stop" | "suspend" | "resume" | "archive";
+ /**
+ * The resource, as its owning authority's resource index lists it (common.resource-summary@1 kind and id).
+ */
+ owned: {
+ kind: string;
+ /**
+ * The summary's id: a prefixed ULID (rob_…) or the service's own slug (a paste, a room).
+ */
+ id: string;
+ };
+ /**
+ * The person the resource belongs to. Person-owned resources belong to people, never to agents or apps.
+ */
+ owner: {
+ type: "user";
+ id: string;
+ };
+ /**
+ * Action-specific input, shaped by the authority's own contract (never redefined here).
+ */
+ params?: {};
+ idempotency_key: string;
+ on_behalf_of?: SubjectRef;
+ /**
+ * The approved confirmation from an earlier confirmation_required answer.
+ */
+ confirmation_id?: string | null;
+ /**
+ * Decide and explain without applying.
+ */
+ dry_run?: boolean;
+ trace_id?: string;
+}
+
/** [email protected] (owner: contracts) */
/** [email protected] (owner: contracts) */
/**
/**
* common.resource-control-result@1: the authority's answer to a common.resource-control-request@1 (ADR-048, plan T13). state is the outcome, not the resource's own state: done (applied), pending (accepted and asynchronous; the authority reports completion later on its events), refused (a policy or confirmation gate said no) or failed (an error). State rules: refused carries problem or confirmation_required; failed carries problem; done and pending carry neither; confirmation_required appears only on refused. result carries the authority's own response document and never another service's rows. The state rules are checked by contracts.resources.checkControlResult.
* common.resource-control-result@1: the authority's answer to a common.resource-control-request@1 (ADR-048, plan T13). state is the outcome, not the resource's own state: done (applied), pending (accepted and asynchronous; the authority reports completion later on its events), refused (a policy or confirmation gate said no) or failed (an error). State rules: refused carries problem or confirmation_required; failed carries problem; done and pending carry neither; confirmation_required appears only on refused. result carries the authority's own response document and never another service's rows. The state rules are checked by contracts.resources.checkControlResult.
@@ -40,7 +40,13 @@ function nameOf(summary) {
@@ -40,7 +40,13 @@ function nameOf(summary) {
function checkName(name) { return registry.validate('common.resource-name@1', name); }
function checkName(name) { return registry.validate('common.resource-name@1', name); }
+/**
+ * A control request: common.resource-control-request@1 for a project's resource (named by its OVRN), or
+ * common.owned-resource-control-request@1 for a person-owned one (named by `owned`, its index kind and id; ADR-048
+ * amendment 2026-10-15). The two are told apart by `owned`.
+ */
function checkControlRequest(q) {
function checkControlRequest(q) {
+ if (q && typeof q === 'object' && q.owned !== undefined) return checkOwnedControlRequest(q);
const r = registry.validate('common.resource-control-request@1', q);
const r = registry.validate('common.resource-control-request@1', q);
if (!r.valid) return r;
if (!r.valid) return r;
const errors = [];
const errors = [];
@@ -51,6 +57,18 @@ function checkControlRequest(q) {
@@ -51,6 +57,18 @@ function checkControlRequest(q) {
return { valid: errors.length === 0, errors };
return { valid: errors.length === 0, errors };
}
}
+/** A person-owned resource's control request: valid, and its kind names the authority's service as a resource kind does. */
+function checkOwnedControlRequest(q) {
+ const r = registry.validate('common.owned-resource-control-request@1', q);
+ if (!r.valid) return r;
+ const errors = [];
+ // An agent may ask for its owner; nobody else may ask for someone's resource.
+ if (q.on_behalf_of && q.on_behalf_of.type === 'user' && q.on_behalf_of.id !== q.owner.id) {
+ errors.push({ path: '/on_behalf_of', message: 'a person acts only on what they own' });
+ }
+ return { valid: errors.length === 0, errors };
+}
+
function checkControlResult(res) {
function checkControlResult(res) {
const r = registry.validate('common.resource-control-result@1', res);
const r = registry.validate('common.resource-control-result@1', res);
if (!r.valid) return r;
if (!r.valid) return r;
@@ -63,4 +81,4 @@ function checkControlResult(res) {
@@ -63,4 +81,4 @@ function checkControlResult(res) {
return { valid: errors.length === 0, errors };
return { valid: errors.length === 0, errors };
}
}
-module.exports = { parse, format, nameOf, checkName, checkControlRequest, checkControlResult };
+module.exports = { parse, format, nameOf, checkName, checkControlRequest, checkOwnedControlRequest, checkControlResult };
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "id": "bot.resource.control",
+ "version": "1.0",
+ "owner": "bot",
+ "status": "active",
+ "visibility": "first-party",
+ "sensitive": true,
+ "description": "Control a person's robots through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a bot.robot (id rob_<ULID>) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: stop (latch the robot's remote e-stop, as POST /api/v1/robots/:id/estop: every actuator stops and nothing moves until it is cleared) and resume (clear that e-stop, as POST /api/v1/robots/:id/estop/clear; a local stop on the robot itself still holds); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",
+ "permissions": [
+ "resources:control"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "standard",
+ "events": [],
+ "implementedBy": [
+ "POST /api/v1/resources/control"
+ ],
+ "inputSchema": "common.owned-resource-control-request@1",
+ "outputSchema": "common.resource-control-result@1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "id": "chat.resource.control",
+ "version": "1.0",
+ "owner": "chat",
+ "status": "active",
+ "visibility": "first-party",
+ "sensitive": true,
+ "description": "Control a person's rooms through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a chat.room (id the room's slug) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: archive (the room leaves every listing and takes no new messages; its history is kept for its members, as an archived room); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",
+ "permissions": [
+ "resources:control"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "standard",
+ "events": [],
+ "implementedBy": [
+ "POST /api/v1/resources/control"
+ ],
+ "inputSchema": "common.owned-resource-control-request@1",
+ "outputSchema": "common.resource-control-result@1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "id": "community.resource.control",
+ "version": "1.0",
+ "owner": "community",
+ "status": "active",
+ "visibility": "first-party",
+ "sensitive": true,
+ "description": "Control a person's pastes through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a community.paste (id the paste's slug) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: delete (the paste goes through Community's own deletion path, as the owner deleting it on openvibe.community); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",
+ "permissions": [
+ "resources:control"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "standard",
+ "events": [],
+ "implementedBy": [
+ "POST /api/v1/resources/control"
+ ],
+ "inputSchema": "common.owned-resource-control-request@1",
+ "outputSchema": "common.resource-control-result@1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "id": "openre.resource.control",
+ "version": "1.0",
+ "owner": "openre",
+ "status": "active",
+ "visibility": "first-party",
+ "sensitive": true,
+ "description": "Control a person's restream definitions through the authority's control API (ADR-048 amendment 2026-10-15, step 8 phase 2): POST /api/v1/resources/control takes one common.owned-resource-control-request@1 naming a openre.stream (id the definition id) from a first-party service (OpenVibe.Services) acting for the person in on_behalf_of. Actions: suspend (state disabled: new ingest is refused, as PATCH state=disabled) and resume (state active again); any other action is refused unsupported. Only the resource's owner may act: the request's owner must be the owner the authority holds. dry_run decides and explains without applying. The first answer per (caller, idempotency_key) is kept and returned for a repeat; the same key with another request is refused. Answers common.resource-control-result@1 without resource (person-owned resources have no OVRN).",
+ "permissions": [
+ "resources:control"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "standard",
+ "events": [],
+ "implementedBy": [
+ "POST /api/v1/resources/control"
+ ],
+ "inputSchema": "common.owned-resource-control-request@1",
+ "outputSchema": "common.resource-control-result@1"
+}
@@ -33,7 +33,8 @@
@@ -33,7 +33,8 @@
"bot.computer.control",
"bot.computer.control",
"bot.device.connect",
"bot.device.connect",
"bot.job.dispatch",
"bot.job.dispatch",
- "bot.resource.read"
+ "bot.resource.read",
+ "bot.resource.control"
],
],
"eventsProduced": [
"eventsProduced": [
"bot.robot.online",
"bot.robot.online",
@@ -26,7 +26,8 @@
@@ -26,7 +26,8 @@
"chat.sounds.write",
"chat.sounds.write",
"chat.stats.read",
"chat.stats.read",
"chat.ticket.write",
"chat.ticket.write",
- "chat.resource.read"
+ "chat.resource.read",
+ "chat.resource.control"
],
],
"eventsProduced": [
"eventsProduced": [
"chat.message.created",
"chat.message.created",
@@ -25,7 +25,8 @@
@@ -25,7 +25,8 @@
"community.thread.read",
"community.thread.read",
"community.vote.set",
"community.vote.set",
"community.pulse.read",
"community.pulse.read",
- "community.resource.read"
+ "community.resource.read",
+ "community.resource.control"
],
],
"eventsProduced": [
"eventsProduced": [
"community.comment.created",
"community.comment.created",
@@ -125,7 +125,8 @@
@@ -125,7 +125,8 @@
"openre.session.end",
"openre.session.end",
"openre.output.read",
"openre.output.read",
"openre.output.write",
"openre.output.write",
- "openre.resource.read"
+ "openre.resource.read",
+ "openre.resource.control"
],
],
"eventsProduced": [
"eventsProduced": [
"openre.session.started",
"openre.session.started",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.175.0",
+ "version": "0.176.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",
@@ -9,7 +9,8 @@ const c = require('..');
@@ -9,7 +9,8 @@ const c = require('..');
// bot.resource.read: the robots in OpenVibe.Services' resource index (planned 0.129.0, active 0.130.0; plan T13 step 8).
// bot.resource.read: the robots in OpenVibe.Services' resource index (planned 0.129.0, active 0.130.0; plan T13 step 8).
// bot.computer.control: an owner's agent uses their own machine (0.166.0, plan T14/T17; Bot's STATUS.json lists it with
// bot.computer.control: an owner's agent uses their own machine (0.166.0, plan T14/T17; Bot's STATUS.json lists it with
// the computer kinds).
// the computer kinds).
-const BOT_CAPABILITIES = ['bot.robot.read', 'bot.robot.manage', 'bot.robot.control', 'bot.computer.control', 'bot.device.connect', 'bot.job.dispatch', 'bot.resource.read'];
+// bot.resource.control: an owner stops or resumes their robot from OpenVibe.Services (0.176.0, ADR-048 step 8 phase 2).
+const BOT_CAPABILITIES = ['bot.robot.read', 'bot.robot.manage', 'bot.robot.control', 'bot.computer.control', 'bot.device.connect', 'bot.job.dispatch', 'bot.resource.read', 'bot.resource.control'];
const BOT_EVENTS = ['bot.robot.online', 'bot.robot.offline', 'bot.estop.set', 'bot.estop.cleared', 'bot.command.refused'];
const BOT_EVENTS = ['bot.robot.online', 'bot.robot.offline', 'bot.estop.set', 'bot.estop.cleared', 'bot.command.refused'];
const bot = c.services.get('bot');
const bot = c.services.get('bot');