openvibers/openvibe.contracts Commits

Contracts 0.108.0: OpenVibe.Watch runs (alpha, capabilities active); four resource indexes active. (#55)

OP OpenVibers committed 7 Oct 2026 · 560e5cc80d · 40 files, +3040 −83

openvibe-contracts 0.108.0: status changes for what now runs in production, no schema change.

- OpenVibe.Watch (deployed on loopback :4730, Watch #1): manifest status alpha, exposure internal (openvibe.watch keeps its Sites placeholder until Watch has a product surface); watch.watch.read/manage, watch.observation.read, watch.check.run active with the routes Watch serves.
- host/events/codes/media.resource.read active, implemented by GET /api/v1/resources and GET /api/v1/resources/:ovrn (Host #28, Events #17, Codes #20, Media #47, all deployed).
- version 0.108.0, CHANGELOG, generated/ (the routes now appear in the OpenAPI documents).

Tests: node test/run.js: 17015 checks passed; compat: no breaking changes.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 3d920454..8643d8c6 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -4,6 +4,16 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
++## 0.108.0 — 2026-10-07
++
++**OpenVibe.Watch runs, and four authorities serve their resource index.** Status changes only; no schema changed.
++- `manifests/services/watch.json`: `status` alpha; `exposure` internal (deployed on loopback :4730 on 2026-10-07, Watch #1);
++ openvibe.watch keeps its OpenVibe.Sites placeholder until Watch has a product surface.
++- `watch.watch.read`, `watch.watch.manage`, `watch.observation.read`, `watch.check.run`: active, with the routes Watch
++ serves (`/api/v1/watches`, `/:id`, pause/resume, `/:id/check`, `/:id/observations`, `/:id/checks`).
++- `host.resource.read`, `events.resource.read`, `codes.resource.read`, `media.resource.read`: active, implemented by
++ `GET /api/v1/resources` and `GET /api/v1/resources/:ovrn` (Host #28, Events #17, Codes #20, Media #47, all deployed).
++
## 0.107.0 — 2026-10-07
**OpenVibe.Host is live at openvibe.host** (Stage B launched 2026-10-07; Host#26, Sites#11): `manifests/services/host.json` `exposure` becomes `{ state: live, publicSite: service }`, so Network lists Host as an open site.
ddiff --git a/generated/openapi/ai.json b/generated/openapi/ai.json
iindex e6d77f9c..e4613282 100644
---- a/generated/openapi/ai.json
++++ b/generated/openapi/ai.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.AI API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/billing.json b/generated/openapi/billing.json
iindex 9a99ebc9..62e925e8 100644
---- a/generated/openapi/billing.json
++++ b/generated/openapi/billing.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Billing API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/blog.json b/generated/openapi/blog.json
iindex 193d8fbe..dae901cc 100644
---- a/generated/openapi/blog.json
++++ b/generated/openapi/blog.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Blog API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/bot.json b/generated/openapi/bot.json
iindex 6e6697e8..22172810 100644
---- a/generated/openapi/bot.json
++++ b/generated/openapi/bot.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Bot API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/chat.json b/generated/openapi/chat.json
iindex e5b32740..8fd2eda1 100644
---- a/generated/openapi/chat.json
++++ b/generated/openapi/chat.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Chat API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/codes.json b/generated/openapi/codes.json
iindex cfecdf36..270a8efd 100644
---- a/generated/openapi/codes.json
++++ b/generated/openapi/codes.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Codes API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for 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"
@@@ -445,6 +445,108 @@
}
}
}
++ },
++ "/api/v1/resources": {
++ "get": {
++ "operationId": "get_api_v1_resources",
++ "summary": "Read OpenVibe.Codes' resource index (ADR-048): GET /api/v1/resources lists the resources Codes owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:o",
++ "description": "**codes.resource.read** (first-party): Read OpenVibe.Codes' resource index (ADR-048): GET /api/v1/resources lists the resources Codes owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Codes' own resources are its validated app and mod manifests (kind codes.manifest, id mfs_<ULID>) and its releases (kind codes.release, id rel_<ULID>), each carrying the Network project it was created in as its tenancy boundary. Trust tiers are per-app metadata and playground run logs are audit records, neither a resource; Codes hosts no repositories, so the catalog carries no codes.repo kind. Planned until Codes ships the index.",
++ "tags": [
++ "codes.resource"
++ ],
++ "x-openvibe-capabilities": [
++ "codes.resource.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "standard",
++ "security": [
++ {
++ "openvibeToken": [
++ "codes.resource.read"
++ ]
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/common.resource-list-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/resources/{ovrn}": {
++ "get": {
++ "operationId": "get_api_v1_resources_ovrn",
++ "summary": "Read OpenVibe.Codes' resource index (ADR-048): GET /api/v1/resources lists the resources Codes owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:o",
++ "description": "**codes.resource.read** (first-party): Read OpenVibe.Codes' resource index (ADR-048): GET /api/v1/resources lists the resources Codes owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Codes' own resources are its validated app and mod manifests (kind codes.manifest, id mfs_<ULID>) and its releases (kind codes.release, id rel_<ULID>), each carrying the Network project it was created in as its tenancy boundary. Trust tiers are per-app metadata and playground run logs are audit records, neither a resource; Codes hosts no repositories, so the catalog carries no codes.repo kind. Planned until Codes ships the index.",
++ "tags": [
++ "codes.resource"
++ ],
++ "x-openvibe-capabilities": [
++ "codes.resource.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "standard",
++ "security": [
++ {
++ "openvibeToken": [
++ "codes.resource.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "ovrn",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/common.resource-list-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
}
},
"components": {
@@@ -929,6 +1031,109 @@
"type": "object",
"maxProperties": 0
},
++ "common.resource-list-result.v1": {
++ "title": "ResourceListResult",
++ "description": "A page of the resource index (roadmap WS-Z7).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "resources",
++ "next_cursor"
++ ],
++ "properties": {
++ "resources": {
++ "type": "array",
++ "items": {
++ "$ref": "#/components/schemas/common.resource-summary.v1"
++ }
++ },
++ "next_cursor": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++ },
++ "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.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "kind",
++ "service",
++ "state",
++ "created_at"
++ ],
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "kind": {
++ "type": "string",
++ "description": "service.kind, e.g. watch.watch, actor.actor, media.object, events.subscription"
++ },
++ "service": {
++ "type": "string"
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "ovrn": {
++ "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}$",
++ "description": "Optional resource name (ADR-034 section 2), the name grants, audit, events, usage records and bills use: ovrn:<service>:<project_id>:<type>/<id>, e.g. ovrn:watch:<project_id>:watch/<wch_id>, with the pattern of common.usage-recorded@1 resource. Its service, project and id are this summary's service, project_id and id. Added in 1.1.0."
++ },
++ "owner": {
++ "$ref": "#/components/schemas/identity.subject-ref.v1"
++ },
++ "name": {
++ "type": "string"
++ },
++ "state": {
++ "type": "string"
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "usage": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "cost units by name (WS-Z1)"
++ },
++ "links": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "self": {
++ "type": "string",
++ "format": "uri"
++ },
++ "console": {
++ "type": "string",
++ "format": "uri"
++ },
++ "docs": {
++ "type": "string",
++ "format": "uri"
++ }
++ }
++ }
++ }
++ },
"errors.problem.v1": {
"title": "Problem",
"description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
ddiff --git a/generated/openapi/community.json b/generated/openapi/community.json
iindex c6a6f26e..ba51afe2 100644
---- a/generated/openapi/community.json
++++ b/generated/openapi/community.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Community API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/coupons.json b/generated/openapi/coupons.json
iindex 9aa2107f..eae2530b 100644
---- a/generated/openapi/coupons.json
++++ b/generated/openapi/coupons.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Coupons API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/deals.json b/generated/openapi/deals.json
iindex 9ee2668b..c107ab55 100644
---- a/generated/openapi/deals.json
++++ b/generated/openapi/deals.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Deals API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/events.json b/generated/openapi/events.json
iindex 761e44f3..6acd0d8b 100644
---- a/generated/openapi/events.json
++++ b/generated/openapi/events.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Events API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for 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"
@@@ -426,6 +426,108 @@
}
}
},
++ "/api/v1/resources": {
++ "get": {
++ "operationId": "get_api_v1_resources",
++ "summary": "Read OpenVibe.Events' resource index (ADR-048): GET /api/v1/resources lists the resources Events owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/",
++ "description": "**events.resource.read** (first-party): Read OpenVibe.Events' resource index (ADR-048): GET /api/v1/resources lists the resources Events owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Events' own resources are its subscriptions (kind events.subscription, id sub_<ULID>), project-scoped when a developer app created them (project_id, with its environment) and project-less when a service did. An event is not a resource — evt_<ULID> is exactly what common.resource-name@1 refuses by design — and a project's queue is a derived carrier class, not a stored row, so it stays out of the index until Events stores a queue row. Planned until Events ships the index.",
++ "tags": [
++ "events.resource"
++ ],
++ "x-openvibe-capabilities": [
++ "events.resource.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "standard",
++ "security": [
++ {
++ "openvibeToken": [
++ "events.resource.read"
++ ]
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/common.resource-list-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/resources/{ovrn}": {
++ "get": {
++ "operationId": "get_api_v1_resources_ovrn",
++ "summary": "Read OpenVibe.Events' resource index (ADR-048): GET /api/v1/resources lists the resources Events owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/",
++ "description": "**events.resource.read** (first-party): Read OpenVibe.Events' resource index (ADR-048): GET /api/v1/resources lists the resources Events owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Events' own resources are its subscriptions (kind events.subscription, id sub_<ULID>), project-scoped when a developer app created them (project_id, with its environment) and project-less when a service did. An event is not a resource — evt_<ULID> is exactly what common.resource-name@1 refuses by design — and a project's queue is a derived carrier class, not a stored row, so it stays out of the index until Events stores a queue row. Planned until Events ships the index.",
++ "tags": [
++ "events.resource"
++ ],
++ "x-openvibe-capabilities": [
++ "events.resource.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "standard",
++ "security": [
++ {
++ "openvibeToken": [
++ "events.resource.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "ovrn",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/common.resource-list-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
"/api/v1/subscriptions": {
"get": {
"operationId": "get_api_v1_subscriptions",
@@@ -818,6 +920,109 @@
"type": "object",
"maxProperties": 0
},
++ "common.resource-list-result.v1": {
++ "title": "ResourceListResult",
++ "description": "A page of the resource index (roadmap WS-Z7).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "resources",
++ "next_cursor"
++ ],
++ "properties": {
++ "resources": {
++ "type": "array",
++ "items": {
++ "$ref": "#/components/schemas/common.resource-summary.v1"
++ }
++ },
++ "next_cursor": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++ },
++ "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.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "kind",
++ "service",
++ "state",
++ "created_at"
++ ],
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "kind": {
++ "type": "string",
++ "description": "service.kind, e.g. watch.watch, actor.actor, media.object, events.subscription"
++ },
++ "service": {
++ "type": "string"
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "ovrn": {
++ "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}$",
++ "description": "Optional resource name (ADR-034 section 2), the name grants, audit, events, usage records and bills use: ovrn:<service>:<project_id>:<type>/<id>, e.g. ovrn:watch:<project_id>:watch/<wch_id>, with the pattern of common.usage-recorded@1 resource. Its service, project and id are this summary's service, project_id and id. Added in 1.1.0."
++ },
++ "owner": {
++ "$ref": "#/components/schemas/identity.subject-ref.v1"
++ },
++ "name": {
++ "type": "string"
++ },
++ "state": {
++ "type": "string"
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "usage": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "cost units by name (WS-Z1)"
++ },
++ "links": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "self": {
++ "type": "string",
++ "format": "uri"
++ },
++ "console": {
++ "type": "string",
++ "format": "uri"
++ },
++ "docs": {
++ "type": "string",
++ "format": "uri"
++ }
++ }
++ }
++ }
++ },
"errors.problem.v1": {
"title": "Problem",
"description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
ddiff --git a/generated/openapi/games.json b/generated/openapi/games.json
iindex 80eac0cc..7894ecd3 100644
---- a/generated/openapi/games.json
++++ b/generated/openapi/games.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Games API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/host.json b/generated/openapi/host.json
iindex 8d283dc4..e98d2b31 100644
---- a/generated/openapi/host.json
++++ b/generated/openapi/host.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Host API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for 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"
@@@ -1109,6 +1109,108 @@
}
}
},
++ "/api/v1/resources": {
++ "get": {
++ "operationId": "get_api_v1_resources",
++ "summary": "Read OpenVibe.Host's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ov",
++ "description": "**host.resource.read** (first-party): Read OpenVibe.Host's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Host's own resources are its static sites (kind host.site, id sit_<ULID>), the immutable deploys of those sites (kind host.deploy, id dpl_<ULID>) and their default and custom domains (kind host.domain, id dom_<ULID>), each with the project_id of the Network project that owns it. Host lists no projects; only Network lists projects. Planned until Host ships the index.",
++ "tags": [
++ "host.resource"
++ ],
++ "x-openvibe-capabilities": [
++ "host.resource.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "standard",
++ "security": [
++ {
++ "openvibeToken": [
++ "host.resource.read"
++ ]
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/common.resource-list-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/resources/{ovrn}": {
++ "get": {
++ "operationId": "get_api_v1_resources_ovrn",
++ "summary": "Read OpenVibe.Host's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ov",
++ "description": "**host.resource.read** (first-party): Read OpenVibe.Host's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Host's own resources are its static sites (kind host.site, id sit_<ULID>), the immutable deploys of those sites (kind host.deploy, id dpl_<ULID>) and their default and custom domains (kind host.domain, id dom_<ULID>), each with the project_id of the Network project that owns it. Host lists no projects; only Network lists projects. Planned until Host ships the index.",
++ "tags": [
++ "host.resource"
++ ],
++ "x-openvibe-capabilities": [
++ "host.resource.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "standard",
++ "security": [
++ {
++ "openvibeToken": [
++ "host.resource.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "ovrn",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/common.resource-list-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
"/api/v1/sites/{id}": {
"delete": {
"operationId": "delete_api_v1_sites_id",
@@@ -2197,6 +2299,109 @@
"type": "object",
"maxProperties": 0
},
++ "common.resource-list-result.v1": {
++ "title": "ResourceListResult",
++ "description": "A page of the resource index (roadmap WS-Z7).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "resources",
++ "next_cursor"
++ ],
++ "properties": {
++ "resources": {
++ "type": "array",
++ "items": {
++ "$ref": "#/components/schemas/common.resource-summary.v1"
++ }
++ },
++ "next_cursor": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++ },
++ "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.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "kind",
++ "service",
++ "state",
++ "created_at"
++ ],
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "kind": {
++ "type": "string",
++ "description": "service.kind, e.g. watch.watch, actor.actor, media.object, events.subscription"
++ },
++ "service": {
++ "type": "string"
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "ovrn": {
++ "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}$",
++ "description": "Optional resource name (ADR-034 section 2), the name grants, audit, events, usage records and bills use: ovrn:<service>:<project_id>:<type>/<id>, e.g. ovrn:watch:<project_id>:watch/<wch_id>, with the pattern of common.usage-recorded@1 resource. Its service, project and id are this summary's service, project_id and id. Added in 1.1.0."
++ },
++ "owner": {
++ "$ref": "#/components/schemas/identity.subject-ref.v1"
++ },
++ "name": {
++ "type": "string"
++ },
++ "state": {
++ "type": "string"
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "usage": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "cost units by name (WS-Z1)"
++ },
++ "links": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "self": {
++ "type": "string",
++ "format": "uri"
++ },
++ "console": {
++ "type": "string",
++ "format": "uri"
++ },
++ "docs": {
++ "type": "string",
++ "format": "uri"
++ }
++ }
++ }
++ }
++ },
"errors.problem.v1": {
"title": "Problem",
"description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
@@@ -3613,6 +3818,123 @@
"format": "date-time"
}
}
++ },
++ "identity.subject-ref.v1": {
++ "title": "SubjectRef",
++ "description": "The one cross-platform reference to an actor. Issued by OpenVibe.Network. Never a service-local integer id.",
++ "oneOf": [
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "user"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "UserSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "guest"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^gst_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "GuestSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "app"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AppSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "mod"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^mod_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "ModSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "enum": [
++ "service",
++ "system"
++ ]
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9-]{1,39}$"
++ }
++ },
++ "description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
++ }
++ ]
}
},
"securitySchemes": {
ddiff --git a/generated/openapi/index.json b/generated/openapi/index.json
iindex 05b779a4..3d7ee5a4 100644
---- a/generated/openapi/index.json
++++ b/generated/openapi/index.json
@@@ -40,8 +40,8 @@
"service": "codes",
"name": "OpenVibe.Codes",
"origin": "https://openvibe.codes",
-- "operations": 7,
-- "capabilities": 2
++ "operations": 9,
++ "capabilities": 3
},
{
"service": "community",
@@@ -68,8 +68,8 @@
"service": "events",
"name": "OpenVibe.Events",
"origin": "https://events.openvibe.network",
-- "operations": 13,
-- "capabilities": 7
++ "operations": 15,
++ "capabilities": 8
},
{
"service": "games",
@@@ -82,8 +82,8 @@
"service": "host",
"name": "OpenVibe.Host",
"origin": "https://openvibe.host",
-- "operations": 34,
-- "capabilities": 4
++ "operations": 36,
++ "capabilities": 5
},
{
"service": "live",
@@@ -96,8 +96,8 @@
"service": "media",
"name": "OpenVibe.Media",
"origin": "https://openvibe.media",
-- "operations": 7,
-- "capabilities": 5
++ "operations": 9,
++ "capabilities": 6
},
{
"service": "network",
@@@ -176,6 +176,13 @@
"operations": 21,
"capabilities": 14
},
++ {
++ "service": "watch",
++ "name": "OpenVibe.Watch",
++ "origin": "https://openvibe.watch",
++ "operations": 10,
++ "capabilities": 4
++ },
{
"service": "wiki",
"name": "OpenVibe.Wiki",
ddiff --git a/generated/openapi/live.json b/generated/openapi/live.json
iindex a03bdeca..4630ba32 100644
---- a/generated/openapi/live.json
++++ b/generated/openapi/live.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Live API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/media.json b/generated/openapi/media.json
iindex 88605639..5c1f57c5 100644
---- a/generated/openapi/media.json
++++ b/generated/openapi/media.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Media API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for 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"
@@@ -146,6 +146,108 @@
}
}
},
++ "/api/v1/resources": {
++ "get": {
++ "operationId": "get_api_v1_resources",
++ "summary": "Read OpenVibe.Media's resource index (ADR-048): GET /api/v1/resources lists the resources Media owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:",
++ "description": "**media.resource.read** (first-party): Read OpenVibe.Media's resource index (ADR-048): GET /api/v1/resources lists the resources Media owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Media's own resources are its objects (kind media.object, id med_<ULID>), the canonical blobs its v1 vods, clips and files are projections of (migrations/0001_initial.sql media_objects); vods and clips are projections over objects (bigint ids) and are never listed. An object carries the project of the tenant that owns it when that tenant is a developer project; an object in a first-party app tenant has no project and so no OVRN. Planned until Media ships the index.",
++ "tags": [
++ "media.resource"
++ ],
++ "x-openvibe-capabilities": [
++ "media.resource.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "standard",
++ "security": [
++ {
++ "openvibeToken": [
++ "media.resource.read"
++ ]
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/common.resource-list-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/resources/{ovrn}": {
++ "get": {
++ "operationId": "get_api_v1_resources_ovrn",
++ "summary": "Read OpenVibe.Media's resource index (ADR-048): GET /api/v1/resources lists the resources Media owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:",
++ "description": "**media.resource.read** (first-party): Read OpenVibe.Media's resource index (ADR-048): GET /api/v1/resources lists the resources Media owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Media's own resources are its objects (kind media.object, id med_<ULID>), the canonical blobs its v1 vods, clips and files are projections of (migrations/0001_initial.sql media_objects); vods and clips are projections over objects (bigint ids) and are never listed. An object carries the project of the tenant that owns it when that tenant is a developer project; an object in a first-party app tenant has no project and so no OVRN. Planned until Media ships the index.",
++ "tags": [
++ "media.resource"
++ ],
++ "x-openvibe-capabilities": [
++ "media.resource.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "standard",
++ "security": [
++ {
++ "openvibeToken": [
++ "media.resource.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "ovrn",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/common.resource-list-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
"/api/v2/{app}/objects": {
"get": {
"operationId": "get_api_v2_app_objects",
@@@ -445,6 +547,109 @@
},
"components": {
"schemas": {
++ "common.resource-list-result.v1": {
++ "title": "ResourceListResult",
++ "description": "A page of the resource index (roadmap WS-Z7).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "resources",
++ "next_cursor"
++ ],
++ "properties": {
++ "resources": {
++ "type": "array",
++ "items": {
++ "$ref": "#/components/schemas/common.resource-summary.v1"
++ }
++ },
++ "next_cursor": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++ },
++ "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.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "kind",
++ "service",
++ "state",
++ "created_at"
++ ],
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "kind": {
++ "type": "string",
++ "description": "service.kind, e.g. watch.watch, actor.actor, media.object, events.subscription"
++ },
++ "service": {
++ "type": "string"
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "ovrn": {
++ "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}$",
++ "description": "Optional resource name (ADR-034 section 2), the name grants, audit, events, usage records and bills use: ovrn:<service>:<project_id>:<type>/<id>, e.g. ovrn:watch:<project_id>:watch/<wch_id>, with the pattern of common.usage-recorded@1 resource. Its service, project and id are this summary's service, project_id and id. Added in 1.1.0."
++ },
++ "owner": {
++ "$ref": "#/components/schemas/identity.subject-ref.v1"
++ },
++ "name": {
++ "type": "string"
++ },
++ "state": {
++ "type": "string"
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "usage": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "cost units by name (WS-Z1)"
++ },
++ "links": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "self": {
++ "type": "string",
++ "format": "uri"
++ },
++ "console": {
++ "type": "string",
++ "format": "uri"
++ },
++ "docs": {
++ "type": "string",
++ "format": "uri"
++ }
++ }
++ }
++ }
++ },
"errors.problem.v1": {
"title": "Problem",
"description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
@@@ -511,6 +716,123 @@
}
}
},
++ "identity.subject-ref.v1": {
++ "title": "SubjectRef",
++ "description": "The one cross-platform reference to an actor. Issued by OpenVibe.Network. Never a service-local integer id.",
++ "oneOf": [
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "user"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "UserSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "guest"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^gst_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "GuestSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "app"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AppSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "mod"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^mod_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "ModSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "enum": [
++ "service",
++ "system"
++ ]
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9-]{1,39}$"
++ }
++ },
++ "description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
++ }
++ ]
++ },
"media.avatar-ingest-request.v1": {
"title": "MediaAvatarIngestRequest",
"description": "media.avatar-ingest-request@1: the body of POST /internal/avatar-ingest on OpenVibe.Media (capability media.avatar.ingest; Network calls it while adopting an avatar a person pointed at somewhere else on the web). Media fetches the URL once through its egress guard (https only, public addresses only, at most 8 MB and 10 s), re-encodes it to a 512×512 WebP and stores it as an unlisted screenshot paste owned by user_id, so no OpenVibe site ever serves or stores a third-party address. Refusals are { ok: false, error } (400 without url or user_id, 422 when the link is not a usable picture).",
ddiff --git a/generated/openapi/network.json b/generated/openapi/network.json
iindex c5890508..ba8780a4 100644
---- a/generated/openapi/network.json
++++ b/generated/openapi/network.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Network API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/news.json b/generated/openapi/news.json
iindex 4b24419f..3189a220 100644
---- a/generated/openapi/news.json
++++ b/generated/openapi/news.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.News API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/openre.json b/generated/openapi/openre.json
iindex 73161cd3..39d609f2 100644
---- a/generated/openapi/openre.json
++++ b/generated/openapi/openre.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenRe.Stream API",
-- "version": "0.107.0",
-- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/reviews.json b/generated/openapi/reviews.json
iindex d3936137..e21251c7 100644
---- a/generated/openapi/reviews.json
++++ b/generated/openapi/reviews.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Reviews API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/search.json b/generated/openapi/search.json
iindex a3726862..fb4d5241 100644
---- a/generated/openapi/search.json
++++ b/generated/openapi/search.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Search API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/sources.json b/generated/openapi/sources.json
iindex af0f191e..50ee0249 100644
---- a/generated/openapi/sources.json
++++ b/generated/openapi/sources.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Sources API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/space.json b/generated/openapi/space.json
iindex f2b4709b..80518359 100644
---- a/generated/openapi/space.json
++++ b/generated/openapi/space.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Space API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/tips.json b/generated/openapi/tips.json
iindex e0493b35..731d1a2e 100644
---- a/generated/openapi/tips.json
++++ b/generated/openapi/tips.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tips API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/tools.json b/generated/openapi/tools.json
iindex 7b536687..fc41ade6 100644
---- a/generated/openapi/tools.json
++++ b/generated/openapi/tools.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tools API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/trade.json b/generated/openapi/trade.json
iindex 4e11d68f..5397acd6 100644
---- a/generated/openapi/trade.json
++++ b/generated/openapi/trade.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Trade API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/vip.json b/generated/openapi/vip.json
iindex d065c2ee..68e2617d 100644
---- a/generated/openapi/vip.json
++++ b/generated/openapi/vip.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.VIP API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/watch.json b/generated/openapi/watch.json
nnew file mode 100644
iindex 00000000..0e897ded
---- /dev/null
++++ b/generated/openapi/watch.json
@@@ -0,0 +1,1860 @@
++{
++ "openapi": "3.1.0",
++ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
++ "info": {
++ "title": "OpenVibe.Watch API",
++ "version": "0.108.0",
++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for 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"
++ }
++ },
++ "servers": [
++ {
++ "url": "https://openvibe.watch"
++ }
++ ],
++ "paths": {
++ "/api/v1/watches": {
++ "get": {
++ "operationId": "get_api_v1_watches",
++ "summary": "PLANNED (plan T18; OpenVibe.Watch is not built yet): List and read the acting person's or project's watches: GET /api/v1/watches and GET /api/v1/watches/:id.",
++ "description": "**watch.watch.read** (first-party): PLANNED (plan T18; OpenVibe.Watch is not built yet): List and read the acting person's or project's watches: GET /api/v1/watches and GET /api/v1/watches/:id. It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the routes.",
++ "tags": [
++ "watch.watch"
++ ],
++ "x-openvibe-capabilities": [
++ "watch.watch.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "watch-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "watch.watch.read"
++ ]
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ },
++ "post": {
++ "operationId": "post_api_v1_watches",
++ "summary": "PLANNED (plan T18; OpenVibe.Watch is not built yet): Create, change, pause, resume and delete a watch: POST /api/v1/watches, PATCH /api/v1/watches/:id, POST /api/v1/watches/:id/pause, POST /api/v1/wat",
++ "description": "**watch.watch.manage** (first-party): PLANNED (plan T18; OpenVibe.Watch is not built yet): Create, change, pause, resume and delete a watch: POST /api/v1/watches, PATCH /api/v1/watches/:id, POST /api/v1/watches/:id/pause, POST /api/v1/watches/:id/resume and DELETE /api/v1/watches/:id. It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the routes.",
++ "tags": [
++ "watch.watch"
++ ],
++ "x-openvibe-capabilities": [
++ "watch.watch.manage"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "watch-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "watch.watch.manage"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch-request.v1"
++ }
++ }
++ }
++ },
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/watches/{id}": {
++ "delete": {
++ "operationId": "delete_api_v1_watches_id",
++ "summary": "PLANNED (plan T18; OpenVibe.Watch is not built yet): Create, change, pause, resume and delete a watch: POST /api/v1/watches, PATCH /api/v1/watches/:id, POST /api/v1/watches/:id/pause, POST /api/v1/wat",
++ "description": "**watch.watch.manage** (first-party): PLANNED (plan T18; OpenVibe.Watch is not built yet): Create, change, pause, resume and delete a watch: POST /api/v1/watches, PATCH /api/v1/watches/:id, POST /api/v1/watches/:id/pause, POST /api/v1/watches/:id/resume and DELETE /api/v1/watches/:id. It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the routes.",
++ "tags": [
++ "watch.watch"
++ ],
++ "x-openvibe-capabilities": [
++ "watch.watch.manage"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "watch-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "watch.watch.manage"
++ ]
++ }
++ ],
++ "x-openvibe-input": {
++ "$ref": "#/components/schemas/watch.watch-request.v1"
++ },
++ "parameters": [
++ {
++ "name": "id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "name",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ }
++ },
++ {
++ "name": "labels",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "string",
++ "maxLength": 60
++ }
++ }
++ },
++ {
++ "name": "source",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/source"
++ }
++ },
++ {
++ "name": "cadence",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/cadence"
++ }
++ },
++ {
++ "name": "extraction",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/extraction"
++ }
++ },
++ {
++ "name": "comparison",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/comparison"
++ }
++ },
++ {
++ "name": "condition",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/condition"
++ }
++ },
++ {
++ "name": "action",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": "array",
++ "minItems": 1,
++ "maxItems": 8,
++ "items": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/action"
++ }
++ }
++ },
++ {
++ "name": "budget",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/budget"
++ }
++ },
++ {
++ "name": "retention",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/retention"
++ }
++ },
++ {
++ "name": "status",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "enum": [
++ "active",
++ "paused",
++ "disabled",
++ "failed"
++ ]
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ },
++ "get": {
++ "operationId": "get_api_v1_watches_id",
++ "summary": "PLANNED (plan T18; OpenVibe.Watch is not built yet): List and read the acting person's or project's watches: GET /api/v1/watches and GET /api/v1/watches/:id.",
++ "description": "**watch.watch.read** (first-party): PLANNED (plan T18; OpenVibe.Watch is not built yet): List and read the acting person's or project's watches: GET /api/v1/watches and GET /api/v1/watches/:id. It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the routes.",
++ "tags": [
++ "watch.watch"
++ ],
++ "x-openvibe-capabilities": [
++ "watch.watch.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "watch-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "watch.watch.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ },
++ "patch": {
++ "operationId": "patch_api_v1_watches_id",
++ "summary": "PLANNED (plan T18; OpenVibe.Watch is not built yet): Create, change, pause, resume and delete a watch: POST /api/v1/watches, PATCH /api/v1/watches/:id, POST /api/v1/watches/:id/pause, POST /api/v1/wat",
++ "description": "**watch.watch.manage** (first-party): PLANNED (plan T18; OpenVibe.Watch is not built yet): Create, change, pause, resume and delete a watch: POST /api/v1/watches, PATCH /api/v1/watches/:id, POST /api/v1/watches/:id/pause, POST /api/v1/watches/:id/resume and DELETE /api/v1/watches/:id. It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the routes.",
++ "tags": [
++ "watch.watch"
++ ],
++ "x-openvibe-capabilities": [
++ "watch.watch.manage"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "watch-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "watch.watch.manage"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch-request.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/watches/{id}/check": {
++ "post": {
++ "operationId": "post_api_v1_watches_id_check",
++ "summary": "PLANNED (plan T18; OpenVibe.Watch is not built yet): Run one check of a watch now, outside its cadence: POST /api/v1/watches/:id/check (409 watch.busy when one is already running).",
++ "description": "**watch.check.run** (first-party): PLANNED (plan T18; OpenVibe.Watch is not built yet): Run one check of a watch now, outside its cadence: POST /api/v1/watches/:id/check (409 watch.busy when one is already running). It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the route.",
++ "tags": [
++ "watch.check"
++ ],
++ "x-openvibe-capabilities": [
++ "watch.check.run"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "watch-run",
++ "security": [
++ {
++ "openvibeToken": [
++ "watch.check.run"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.check-run.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/watches/{id}/checks": {
++ "get": {
++ "operationId": "get_api_v1_watches_id_checks",
++ "summary": "PLANNED (plan T18; OpenVibe.Watch is not built yet): Read a watch's observations and check runs: GET /api/v1/watches/:id/observations and GET /api/v1/watches/:id/checks.",
++ "description": "**watch.observation.read** (first-party): PLANNED (plan T18; OpenVibe.Watch is not built yet): Read a watch's observations and check runs: GET /api/v1/watches/:id/observations and GET /api/v1/watches/:id/checks. It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the routes.",
++ "tags": [
++ "watch.observation"
++ ],
++ "x-openvibe-capabilities": [
++ "watch.observation.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "watch-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "watch.observation.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.observation.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/watches/{id}/observations": {
++ "get": {
++ "operationId": "get_api_v1_watches_id_observations",
++ "summary": "PLANNED (plan T18; OpenVibe.Watch is not built yet): Read a watch's observations and check runs: GET /api/v1/watches/:id/observations and GET /api/v1/watches/:id/checks.",
++ "description": "**watch.observation.read** (first-party): PLANNED (plan T18; OpenVibe.Watch is not built yet): Read a watch's observations and check runs: GET /api/v1/watches/:id/observations and GET /api/v1/watches/:id/checks. It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the routes.",
++ "tags": [
++ "watch.observation"
++ ],
++ "x-openvibe-capabilities": [
++ "watch.observation.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "watch-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "watch.observation.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.observation.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/watches/{id}/pause": {
++ "post": {
++ "operationId": "post_api_v1_watches_id_pause",
++ "summary": "PLANNED (plan T18; OpenVibe.Watch is not built yet): Create, change, pause, resume and delete a watch: POST /api/v1/watches, PATCH /api/v1/watches/:id, POST /api/v1/watches/:id/pause, POST /api/v1/wat",
++ "description": "**watch.watch.manage** (first-party): PLANNED (plan T18; OpenVibe.Watch is not built yet): Create, change, pause, resume and delete a watch: POST /api/v1/watches, PATCH /api/v1/watches/:id, POST /api/v1/watches/:id/pause, POST /api/v1/watches/:id/resume and DELETE /api/v1/watches/:id. It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the routes.",
++ "tags": [
++ "watch.watch"
++ ],
++ "x-openvibe-capabilities": [
++ "watch.watch.manage"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "watch-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "watch.watch.manage"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch-request.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/watches/{id}/resume": {
++ "post": {
++ "operationId": "post_api_v1_watches_id_resume",
++ "summary": "PLANNED (plan T18; OpenVibe.Watch is not built yet): Create, change, pause, resume and delete a watch: POST /api/v1/watches, PATCH /api/v1/watches/:id, POST /api/v1/watches/:id/pause, POST /api/v1/wat",
++ "description": "**watch.watch.manage** (first-party): PLANNED (plan T18; OpenVibe.Watch is not built yet): Create, change, pause, resume and delete a watch: POST /api/v1/watches, PATCH /api/v1/watches/:id, POST /api/v1/watches/:id/pause, POST /api/v1/watches/:id/resume and DELETE /api/v1/watches/:id. It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the routes.",
++ "tags": [
++ "watch.watch"
++ ],
++ "x-openvibe-capabilities": [
++ "watch.watch.manage"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "watch-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "watch.watch.manage"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch-request.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/watch.watch-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "components": {
++ "schemas": {
++ "errors.problem.v1": {
++ "title": "Problem",
++ "description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
++ "type": "object",
++ "required": [
++ "type",
++ "title",
++ "status",
++ "code"
++ ],
++ "additionalProperties": true,
++ "properties": {
++ "type": {
++ "type": "string",
++ "format": "uri-reference"
++ },
++ "title": {
++ "type": "string",
++ "minLength": 1
++ },
++ "status": {
++ "type": "integer",
++ "minimum": 400,
++ "maximum": 599
++ },
++ "detail": {
++ "type": "string"
++ },
++ "instance": {
++ "type": "string",
++ "format": "uri-reference"
++ },
++ "code": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$"
++ },
++ "request_id": {
++ "type": "string"
++ },
++ "trace_id": {
++ "type": "string",
++ "pattern": "^[0-9a-f]{32}$"
++ },
++ "errors": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "required": [
++ "message"
++ ],
++ "properties": {
++ "path": {
++ "type": "string"
++ },
++ "message": {
++ "type": "string"
++ }
++ }
++ }
++ },
++ "error": {
++ "type": "string",
++ "description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."
++ }
++ }
++ },
++ "platform.workload-requirements.v1": {
++ "title": "WorkloadRequirements",
++ "description": "What a workload needs, so the platform can place it (roadmap WS-Z9). Hard constraints are filtered before any objective is scored; a cheaper candidate that misses one is never chosen.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "kind",
++ "mobility",
++ "latency_class",
++ "objective"
++ ],
++ "properties": {
++ "kind": {
++ "type": "string",
++ "description": "what runs, e.g. watch.browser, media.transcode, ai.run, events.deliver"
++ },
++ "mobility": {
++ "type": "string",
++ "enum": [
++ "request",
++ "job",
++ "session",
++ "stateful-partition"
++ ]
++ },
++ "latency_class": {
++ "type": "string",
++ "enum": [
++ "realtime",
++ "interactive",
++ "background",
++ "bulk",
++ "critical"
++ ]
++ },
++ "objective": {
++ "type": "string",
++ "enum": [
++ "cheapest",
++ "lowest-latency",
++ "balanced",
++ "private",
++ "local-only",
++ "first-party-only",
++ "high-reliability",
++ "correctness"
++ ]
++ },
++ "max_latency_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "deadline_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "max_cost_usd": {
++ "type": "number",
++ "minimum": 0
++ },
++ "durability": {
++ "type": "string",
++ "enum": [
++ "none",
++ "at-least-once",
++ "authoritative"
++ ]
++ },
++ "residency": {
++ "type": "string",
++ "description": "region or jurisdiction the work and its data must stay in"
++ },
++ "trust": {
++ "type": "array",
++ "items": {
++ "type": "string",
++ "enum": [
++ "first-party",
++ "user-owned",
++ "partner",
++ "community",
++ "external"
++ ]
++ },
++ "minItems": 1,
++ "description": "trust classes allowed to run it (ADR-046); when absent, first-party, partner, community and external. user-owned is never a default: a user-owned offer is eligible only when this list names it"
++ },
++ "region": {
++ "type": "string",
++ "description": "a region, or 'nearest'"
++ },
++ "capabilities": {
++ "type": "array",
++ "items": {
++ "type": "string"
++ },
++ "description": "capabilities a candidate must offer (worker:browser, …)"
++ },
++ "resources": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "cpu": {
++ "type": "number",
++ "minimum": 0
++ },
++ "memory_mb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "gpu": {
++ "type": "boolean"
++ },
++ "scratch_gb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "egress": {
++ "type": "string",
++ "enum": [
++ "none",
++ "public",
++ "openvibe-only"
++ ]
++ }
++ }
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "partition_key": {
++ "type": "string",
++ "description": "stable key for rendezvous placement (room, stream, session)"
++ }
++ }
++ },
++ "watch.check-run.v1": {
++ "title": "WatchCheckRun",
++ "description": "watch.check-run@1: one execution of a watch's check (plan T18), by schedule, manually, or from a delivered event or webhook. carrier names the mechanism that carried it; state is the terminal state (the same values the service's check_runs.state CHECK allows). observations is how many observations the run recorded and triggers how many conditions it fired; cost_usd is what the run cost. Rows are immutable.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "watch_id",
++ "trigger",
++ "carrier",
++ "started_at",
++ "finished_at",
++ "state",
++ "http_status",
++ "observations",
++ "triggers",
++ "cost_usd",
++ "detail"
++ ],
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^ckr_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "watch_id": {
++ "type": "string",
++ "pattern": "^wch_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "trigger": {
++ "enum": [
++ "schedule",
++ "manual",
++ "event",
++ "webhook"
++ ]
++ },
++ "carrier": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "enum": [
++ "event",
++ "webhook",
++ "http",
++ "feed",
++ "api",
++ "node",
++ "run",
++ null
++ ]
++ },
++ "started_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "finished_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "state": {
++ "enum": [
++ "ok",
++ "not_modified",
++ "no_change",
++ "changed",
++ "condition_met",
++ "http_error",
++ "timeout",
++ "parse_error",
++ "rate_limited",
++ "budget_exceeded",
++ "skipped",
++ "disabled"
++ ]
++ },
++ "http_status": {
++ "type": [
++ "integer",
++ "null"
++ ]
++ },
++ "observations": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "triggers": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "cost_usd": {
++ "type": "number",
++ "minimum": 0
++ },
++ "detail": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++ },
++ "watch.observation.v1": {
++ "title": "WatchObservation",
++ "description": "watch.observation@1: one value a check recorded for a watch (plan T18): what was extracted, when, from which check run, and whether it changed or met the condition. value is the extracted value exactly as the source stated it, never inferred; snapshot is a capped copy of the raw body kept according to the watch's retention. Rows are immutable.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "watch_id",
++ "check_run_id",
++ "observed_at",
++ "value",
++ "value_hash",
++ "previous_hash",
++ "changed",
++ "condition_met",
++ "snapshot"
++ ],
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^wco_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "watch_id": {
++ "type": "string",
++ "pattern": "^wch_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "check_run_id": {
++ "type": "string",
++ "pattern": "^ckr_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "observed_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "value": {
++ "description": "The extracted value as the source stated it: text, a decimal number, a structured object."
++ },
++ "value_hash": {
++ "type": "string",
++ "pattern": "^[0-9a-f]{64}$",
++ "description": "SHA-256 hex of the canonical value."
++ },
++ "previous_hash": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "pattern": "^[0-9a-f]{64}$",
++ "description": "SHA-256 of the previous observation's value; null for the first observation."
++ },
++ "changed": {
++ "type": "boolean"
++ },
++ "condition_met": {
++ "type": "boolean"
++ },
++ "snapshot": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++ },
++ "watch.watch-request.v1": {
++ "title": "WatchRequest",
++ "description": "watch.watch-request@1: the body of POST /api/v1/watches and PATCH /api/v1/watches/:id on OpenVibe.Watch (watch.watch.manage; the acting person). Every field is optional on a patch; a create requires source, extraction, condition and action, which the service enforces in code (422 watch.invalid).",
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "name": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "labels": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "string",
++ "maxLength": 60
++ }
++ },
++ "source": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/source"
++ },
++ "cadence": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/cadence"
++ },
++ "extraction": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/extraction"
++ },
++ "comparison": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/comparison"
++ },
++ "condition": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/condition"
++ },
++ "action": {
++ "type": "array",
++ "minItems": 1,
++ "maxItems": 8,
++ "items": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/action"
++ }
++ },
++ "budget": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/budget"
++ },
++ "retention": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/retention"
++ },
++ "status": {
++ "enum": [
++ "active",
++ "paused",
++ "disabled",
++ "failed"
++ ]
++ }
++ }
++ },
++ "watch.watch-result.v1": {
++ "title": "WatchResult",
++ "description": "watch.watch-result@1: answers of the watch routes on OpenVibe.Watch. GET /api/v1/watches → { watches } and POST /api/v1/watches → 201 { watch } (watch.watch.read, watch.watch.manage); PATCH → { watch }; POST /:id/pause and /:id/resume → { id, status }; DELETE /api/v1/watches/:id → { id, deleted: true }.",
++ "anyOf": [
++ {
++ "type": "object",
++ "required": [
++ "watches"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "watches": {
++ "type": "array",
++ "items": {
++ "$ref": "#/components/schemas/watch.watch.v1"
++ }
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "watch"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "watch": {
++ "$ref": "#/components/schemas/watch.watch.v1"
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "id",
++ "status"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^wch_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "status": {
++ "enum": [
++ "active",
++ "paused",
++ "disabled",
++ "failed"
++ ]
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "id",
++ "deleted"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^wch_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "deleted": {
++ "const": true
++ }
++ }
++ }
++ ]
++ },
++ "watch.watch.v1": {
++ "title": "Watch",
++ "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Node probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "owner",
++ "status",
++ "source",
++ "extraction",
++ "condition",
++ "action",
++ "created_at",
++ "updated_at"
++ ],
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^wch_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "project_id": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "owner": {
++ "type": "string",
++ "description": "The acting person or agent the watch belongs to: usr_…, agt_… or svc:…."
++ },
++ "name": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "labels": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "string",
++ "maxLength": 60
++ }
++ },
++ "status": {
++ "enum": [
++ "active",
++ "paused",
++ "disabled",
++ "failed"
++ ]
++ },
++ "source": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/source"
++ },
++ "cadence": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/cadence"
++ },
++ "extraction": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/extraction"
++ },
++ "comparison": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/comparison"
++ },
++ "condition": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/condition"
++ },
++ "action": {
++ "type": "array",
++ "minItems": 1,
++ "maxItems": 8,
++ "items": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/action"
++ }
++ },
++ "budget": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/budget"
++ },
++ "retention": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/retention"
++ },
++ "health": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": true
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ },
++ "$defs": {
++ "auth": {
++ "type": "object",
++ "required": [
++ "mode"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "mode": {
++ "enum": [
++ "none",
++ "header",
++ "bearer",
++ "query"
++ ]
++ },
++ "env": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "pattern": "^WATCH_CRED_[A-Z0-9_]{2,60}$",
++ "description": "The name of the environment variable holding the credential; Watch reads it by name at fetch time and never stores the value."
++ },
++ "header": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "param": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++ },
++ "source": {
++ "oneOf": [
++ {
++ "type": "object",
++ "required": [
++ "kind"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "const": "webhook"
++ },
++ "endpoint": {
++ "type": "string",
++ "pattern": "^/internal/hooks/"
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "kind",
++ "pattern"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "const": "event"
++ },
++ "pattern": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_*]+){1,}$"
++ },
++ "filter": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": {
++ "type": [
++ "string",
++ "number",
++ "boolean",
++ "null"
++ ]
++ }
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "kind",
++ "url"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "enum": [
++ "http",
++ "feed",
++ "api"
++ ]
++ },
++ "url": {
++ "type": "string",
++ "format": "uri",
++ "pattern": "^https?://"
++ },
++ "method": {
++ "enum": [
++ "GET",
++ "HEAD"
++ ]
++ },
++ "format": {
++ "enum": [
++ "rss",
++ "atom",
++ "json",
++ null
++ ]
++ },
++ "items_path": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "fields": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": {
++ "type": "string"
++ }
++ },
++ "auth": {
++ "$ref": "#/components/schemas/watch.watch.v1/$defs/auth"
++ },
++ "headers": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "string"
++ }
++ },
++ "conditional": {
++ "type": "boolean",
++ "description": "Send If-None-Match / If-Modified-Since from the stored ETag/Last-Modified when the server gave one."
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "kind",
++ "node_id",
++ "probe"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "const": "node"
++ },
++ "node_id": {
++ "type": "string"
++ },
++ "probe": {
++ "type": "object",
++ "additionalProperties": true
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "kind",
++ "runtime",
++ "steps"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "const": "run"
++ },
++ "runtime": {
++ "enum": [
++ "browser",
++ "code",
++ "desktop"
++ ]
++ },
++ "url": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "uri"
++ },
++ "steps": {
++ "type": "array",
++ "minItems": 1,
++ "items": {
++ "type": "object",
++ "additionalProperties": true
++ }
++ },
++ "workload": {
++ "$ref": "#/components/schemas/platform.workload-requirements.v1"
++ }
++ }
++ }
++ ]
++ },
++ "cadence": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "description": "Required for http/feed/api/node sources; null for event/webhook sources (they never poll).",
++ "properties": {
++ "every_sec": {
++ "type": "integer",
++ "minimum": 30,
++ "maximum": 2592000
++ },
++ "jitter_sec": {
++ "type": "integer",
++ "minimum": 0,
++ "maximum": 3600
++ },
++ "timezone": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++ },
++ "extraction": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "enum": [
++ "text",
++ "html",
++ "json",
++ "regex",
++ "css",
++ "jsonpath",
++ "ai"
++ ]
++ },
++ "selector": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "fields": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": {
++ "type": "string"
++ }
++ },
++ "value_path": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "ai": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": true
++ }
++ }
++ },
++ "comparison": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "mode": {
++ "enum": [
++ "changed",
++ "threshold",
++ "none"
++ ]
++ },
++ "tolerance": {
++ "type": [
++ "number",
++ "null"
++ ]
++ },
++ "percent": {
++ "type": [
++ "number",
++ "null"
++ ]
++ }
++ }
++ },
++ "condition": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "op": {
++ "enum": [
++ "changed",
++ "gt",
++ "gte",
++ "lt",
++ "lte",
++ "eq",
++ "ne",
++ "contains",
++ "matches",
++ "exists",
++ "absent"
++ ]
++ },
++ "value": {
++ "type": [
++ "string",
++ "number",
++ "null"
++ ]
++ },
++ "for_sec": {
++ "type": [
++ "integer",
++ "null"
++ ],
++ "minimum": 0
++ }
++ }
++ },
++ "action": {
++ "oneOf": [
++ {
++ "type": "object",
++ "required": [
++ "kind"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "const": "notification"
++ },
++ "category": {
++ "enum": [
++ "service",
++ "social",
++ "system"
++ ]
++ },
++ "priority": {
++ "enum": [
++ "low",
++ "normal",
++ "high",
++ "critical"
++ ]
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "kind",
++ "event_type"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "const": "event"
++ },
++ "event_type": {
++ "type": "string"
++ },
++ "payload": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": true
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "kind",
++ "url",
++ "secret_env"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "const": "webhook"
++ },
++ "url": {
++ "type": "string",
++ "format": "uri",
++ "pattern": "^https://"
++ },
++ "secret_env": {
++ "type": "string",
++ "pattern": "^WATCH_HOOK_[A-Z0-9_]{2,60}$"
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "kind",
++ "brief"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "const": "actor-task"
++ },
++ "brief": {
++ "type": "string",
++ "maxLength": 8000
++ },
++ "budget_usd": {
++ "type": [
++ "number",
++ "null"
++ ]
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "kind",
++ "repo",
++ "brief"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "const": "codes-run"
++ },
++ "repo": {
++ "type": "string"
++ },
++ "brief": {
++ "type": "string",
++ "maxLength": 8000
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "kind",
++ "runtime",
++ "command"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "kind": {
++ "const": "run-job"
++ },
++ "runtime": {
++ "enum": [
++ "function",
++ "code",
++ "browser",
++ "linux"
++ ]
++ },
++ "command": {
++ "type": "string"
++ },
++ "workload": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": true
++ }
++ }
++ }
++ ]
++ },
++ "budget": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "checks_per_day": {
++ "type": [
++ "integer",
++ "null"
++ ],
++ "minimum": 1
++ },
++ "usd_per_month": {
++ "type": [
++ "number",
++ "null"
++ ],
++ "minimum": 0
++ },
++ "max_runtime_ms": {
++ "type": [
++ "integer",
++ "null"
++ ],
++ "minimum": 100
++ },
++ "max_bytes": {
++ "type": [
++ "integer",
++ "null"
++ ],
++ "minimum": 1024
++ }
++ }
++ },
++ "retention": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "observations_days": {
++ "type": [
++ "integer",
++ "null"
++ ],
++ "minimum": 1
++ },
++ "keep_snapshots": {
++ "type": "boolean"
++ }
++ }
++ }
++ }
++ }
++ },
++ "securitySchemes": {
++ "openvibeToken": {
++ "type": "http",
++ "scheme": "bearer",
++ "bearerFormat": "JWT",
++ "description": "An OpenVibe Network token for this service: a person's token, an app token (client credentials, audience of the service) or a first-party service token. Its `cap` claim must include the capability the operation names."
++ }
++ }
++ }
++}
ddiff --git a/generated/openapi/wiki.json b/generated/openapi/wiki.json
iindex 4f2c7017..24b14e0f 100644
---- a/generated/openapi/wiki.json
++++ b/generated/openapi/wiki.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Wiki API",
-- "version": "0.107.0",
-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.107.0): each operation lists the capabilities it performs; a token for 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.108.0",
++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.108.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/manifests/capabilities/codes.resource.read.json b/manifests/capabilities/codes.resource.read.json
iindex d30e2e4c..0745fa43 100644
---- a/manifests/capabilities/codes.resource.read.json
++++ b/manifests/capabilities/codes.resource.read.json
@@@ -2,7 +2,7 @@
"id": "codes.resource.read",
"version": "1.0",
"owner": "codes",
-- "status": "planned",
++ "status": "active",
"visibility": "first-party",
"description": "Read OpenVibe.Codes' resource index (ADR-048): GET /api/v1/resources lists the resources Codes owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Codes' own resources are its validated app and mod manifests (kind codes.manifest, id mfs_<ULID>) and its releases (kind codes.release, id rel_<ULID>), each carrying the Network project it was created in as its tenancy boundary. Trust tiers are per-app metadata and playground run logs are audit records, neither a resource; Codes hosts no repositories, so the catalog carries no codes.repo kind. Planned until Codes ships the index.",
"permissions": [
@@@ -13,7 +13,10 @@
],
"quotaClass": "standard",
"events": [],
-- "implementedBy": [],
++ "implementedBy": [
++ "GET /api/v1/resources",
++ "GET /api/v1/resources/:ovrn"
++ ],
"inputSchema": "common.no-body@1",
"outputSchema": "common.resource-list-result@1"
}
ddiff --git a/manifests/capabilities/events.resource.read.json b/manifests/capabilities/events.resource.read.json
iindex c6f453e9..2acdbb5d 100644
---- a/manifests/capabilities/events.resource.read.json
++++ b/manifests/capabilities/events.resource.read.json
@@@ -2,7 +2,7 @@
"id": "events.resource.read",
"version": "1.0",
"owner": "events",
-- "status": "planned",
++ "status": "active",
"visibility": "first-party",
"description": "Read OpenVibe.Events' resource index (ADR-048): GET /api/v1/resources lists the resources Events owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Events' own resources are its subscriptions (kind events.subscription, id sub_<ULID>), project-scoped when a developer app created them (project_id, with its environment) and project-less when a service did. An event is not a resource — evt_<ULID> is exactly what common.resource-name@1 refuses by design — and a project's queue is a derived carrier class, not a stored row, so it stays out of the index until Events stores a queue row. Planned until Events ships the index.",
"permissions": [
@@@ -13,7 +13,10 @@
],
"quotaClass": "standard",
"events": [],
-- "implementedBy": [],
++ "implementedBy": [
++ "GET /api/v1/resources",
++ "GET /api/v1/resources/:ovrn"
++ ],
"inputSchema": "common.no-body@1",
"outputSchema": "common.resource-list-result@1"
}
ddiff --git a/manifests/capabilities/host.resource.read.json b/manifests/capabilities/host.resource.read.json
iindex b8d94d45..a9a56b5f 100644
---- a/manifests/capabilities/host.resource.read.json
++++ b/manifests/capabilities/host.resource.read.json
@@@ -2,7 +2,7 @@
"id": "host.resource.read",
"version": "1.0",
"owner": "host",
-- "status": "planned",
++ "status": "active",
"visibility": "first-party",
"description": "Read OpenVibe.Host's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Host's own resources are its static sites (kind host.site, id sit_<ULID>), the immutable deploys of those sites (kind host.deploy, id dpl_<ULID>) and their default and custom domains (kind host.domain, id dom_<ULID>), each with the project_id of the Network project that owns it. Host lists no projects; only Network lists projects. Planned until Host ships the index.",
"permissions": [
@@@ -13,7 +13,10 @@
],
"quotaClass": "standard",
"events": [],
-- "implementedBy": [],
++ "implementedBy": [
++ "GET /api/v1/resources",
++ "GET /api/v1/resources/:ovrn"
++ ],
"inputSchema": "common.no-body@1",
"outputSchema": "common.resource-list-result@1"
}
ddiff --git a/manifests/capabilities/media.resource.read.json b/manifests/capabilities/media.resource.read.json
iindex 2003502a..ebc290e5 100644
---- a/manifests/capabilities/media.resource.read.json
++++ b/manifests/capabilities/media.resource.read.json
@@@ -2,7 +2,7 @@
"id": "media.resource.read",
"version": "1.0",
"owner": "media",
-- "status": "planned",
++ "status": "active",
"visibility": "first-party",
"description": "Read OpenVibe.Media's resource index (ADR-048): GET /api/v1/resources lists the resources Media owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Media's own resources are its objects (kind media.object, id med_<ULID>), the canonical blobs its v1 vods, clips and files are projections of (migrations/0001_initial.sql media_objects); vods and clips are projections over objects (bigint ids) and are never listed. An object carries the project of the tenant that owns it when that tenant is a developer project; an object in a first-party app tenant has no project and so no OVRN. Planned until Media ships the index.",
"permissions": [
@@@ -13,7 +13,10 @@
],
"quotaClass": "standard",
"events": [],
-- "implementedBy": [],
++ "implementedBy": [
++ "GET /api/v1/resources",
++ "GET /api/v1/resources/:ovrn"
++ ],
"inputSchema": "common.no-body@1",
"outputSchema": "common.resource-list-result@1"
}
ddiff --git a/manifests/capabilities/watch.check.run.json b/manifests/capabilities/watch.check.run.json
iindex 4e5e6ba9..2184d0c3 100644
---- a/manifests/capabilities/watch.check.run.json
++++ b/manifests/capabilities/watch.check.run.json
@@@ -2,7 +2,7 @@
"id": "watch.check.run",
"version": "1.0",
"owner": "watch",
-- "status": "planned",
++ "status": "active",
"visibility": "first-party",
"description": "PLANNED (plan T18; OpenVibe.Watch is not built yet): Run one check of a watch now, outside its cadence: POST /api/v1/watches/:id/check (409 watch.busy when one is already running). It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the route.",
"permissions": [
@@@ -13,7 +13,9 @@
],
"quotaClass": "watch-run",
"events": [],
-- "implementedBy": [],
++ "implementedBy": [
++ "POST /api/v1/watches/:id/check"
++ ],
"inputSchema": "common.no-body@1",
"outputSchema": "watch.check-run@1"
}
ddiff --git a/manifests/capabilities/watch.observation.read.json b/manifests/capabilities/watch.observation.read.json
iindex 87e415a7..988132c5 100644
---- a/manifests/capabilities/watch.observation.read.json
++++ b/manifests/capabilities/watch.observation.read.json
@@@ -2,7 +2,7 @@
"id": "watch.observation.read",
"version": "1.0",
"owner": "watch",
-- "status": "planned",
++ "status": "active",
"visibility": "first-party",
"description": "PLANNED (plan T18; OpenVibe.Watch is not built yet): Read a watch's observations and check runs: GET /api/v1/watches/:id/observations and GET /api/v1/watches/:id/checks. It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the routes.",
"permissions": [
@@@ -13,7 +13,10 @@
],
"quotaClass": "watch-read",
"events": [],
-- "implementedBy": [],
++ "implementedBy": [
++ "GET /api/v1/watches/:id/observations",
++ "GET /api/v1/watches/:id/checks"
++ ],
"inputSchema": "common.no-body@1",
"outputSchema": "watch.observation@1"
}
ddiff --git a/manifests/capabilities/watch.watch.manage.json b/manifests/capabilities/watch.watch.manage.json
iindex a6c970fa..6a6b9608 100644
---- a/manifests/capabilities/watch.watch.manage.json
++++ b/manifests/capabilities/watch.watch.manage.json
@@@ -2,7 +2,7 @@
"id": "watch.watch.manage",
"version": "1.0",
"owner": "watch",
-- "status": "planned",
++ "status": "active",
"visibility": "first-party",
"description": "PLANNED (plan T18; OpenVibe.Watch is not built yet): Create, change, pause, resume and delete a watch: POST /api/v1/watches, PATCH /api/v1/watches/:id, POST /api/v1/watches/:id/pause, POST /api/v1/watches/:id/resume and DELETE /api/v1/watches/:id. It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the routes.",
"permissions": [
@@@ -18,7 +18,13 @@
"watch.watch.paused",
"watch.watch.removed"
],
-- "implementedBy": [],
++ "implementedBy": [
++ "POST /api/v1/watches",
++ "PATCH /api/v1/watches/:id",
++ "POST /api/v1/watches/:id/pause",
++ "POST /api/v1/watches/:id/resume",
++ "DELETE /api/v1/watches/:id"
++ ],
"inputSchema": "watch.watch-request@1",
"outputSchema": "watch.watch-result@1"
}
ddiff --git a/manifests/capabilities/watch.watch.read.json b/manifests/capabilities/watch.watch.read.json
iindex 137c0df4..7eee34d8 100644
---- a/manifests/capabilities/watch.watch.read.json
++++ b/manifests/capabilities/watch.watch.read.json
@@@ -2,7 +2,7 @@
"id": "watch.watch.read",
"version": "1.0",
"owner": "watch",
-- "status": "planned",
++ "status": "active",
"visibility": "first-party",
"description": "PLANNED (plan T18; OpenVibe.Watch is not built yet): List and read the acting person's or project's watches: GET /api/v1/watches and GET /api/v1/watches/:id. It becomes active, and enters generated/openapi/watch.json, when OpenVibe.Watch implements the routes.",
"permissions": [
@@@ -13,7 +13,10 @@
],
"quotaClass": "watch-read",
"events": [],
-- "implementedBy": [],
++ "implementedBy": [
++ "GET /api/v1/watches",
++ "GET /api/v1/watches/:id"
++ ],
"inputSchema": "common.no-body@1",
"outputSchema": "watch.watch-result@1"
}
ddiff --git a/manifests/services/watch.json b/manifests/services/watch.json
iindex 4093328d..c576c1de 100644
---- a/manifests/services/watch.json
++++ b/manifests/services/watch.json
@@@ -5,16 +5,16 @@
},
"id": "watch",
"name": "OpenVibe.Watch",
-- "status": "placeholder",
++ "status": "alpha",
"repository": "OpenVibers/OpenVibe.Watch",
"domains": [
"openvibe.watch"
],
"publicOrigin": "https://openvibe.watch",
"exposure": {
-- "state": "placeholder",
-- "publicSite": null,
-- "note": "planned (plan T18); the OpenVibe.Watch repository does not exist yet, and nothing serves openvibe.watch"
++ "state": "internal",
++ "publicSite": "placeholder",
++ "note": "deployed on loopback :4730 (2026-10-07); openvibe.watch keeps its OpenVibe.Sites placeholder until Watch has a product surface"
},
"capabilities": [
"watch.watch.read",
ddiff --git a/package-lock.json b/package-lock.json
iindex d19e1af9..686ca912 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-contracts",
-- "version": "0.107.0",
++ "version": "0.108.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-contracts",
-- "version": "0.107.0",
++ "version": "0.108.0",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
ddiff --git a/package.json b/package.json
iindex 1fa62388..85ed0815 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.107.0",
++ "version": "0.108.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",