openvibers/openvibe.contracts Commits
0.315.0: the seven shipped config.manage capabilities and the AI editor review capabilities (#323)
Changes
@@@ -1,5 +1,11 @@# Changelog++## 0.315.0 — 2026-11-07++++- The owner's Setup tab reaches seven more services: `<service>.config.manage` for `chat`, `connect`, `deals`, `work`, `sources`, `design` and `domains` is now **active** and listed in each service's manifest, because each mounts openvibe-shared/config's routes on `/internal/config` (`GET /internal/config`, `GET|POST /internal/config/:namespace`, `PUT|DELETE /internal/config/:namespace/secrets/:key`, `POST /internal/config/:namespace/test/:key`). Only Network's admin proxy, acting for the owner, holds them and they are never grantable to apps. `media.config.manage` stays planned until Media mounts the routes.++- Add `blog.review.ai_editor`, `news.review.ai_editor` and `wiki.review.ai_editor`: the editorial queue's AI editor decision (approved or rejected; approved or needs changes on Wiki) recorded by the first-party service principal acting as itself (a `svc:` principal with no `X-OV-Subject`) through Blog's `POST /api/v1/posts/:id/reviews`, News's `POST /api/v1/stories/:id/reviews` and `POST /api/v1/briefs/:id/reviews`, and Wiki's `POST /api/v1/pages/:id/revisions/:n/review`. An approval lets AI-written text publish and index and the page says "Checked by OpenVibe's AI editor" with its date, never "Reviewed by a person"; each is granted only to the orchestrator's service principal. Their requests and results are a recorded schema gap (`compatibility/capability-schema-gaps.json`).++- `generated/` refreshed: Connect and Design gain an OpenAPI document now that each has an active capability, and Blog, Chat, Deals, Domains, News, Sources, Wiki and Work gain the new operations.++## 0.314.0 — 2026-11-07- Add the capability `live.agent_sandbox.register` (Agent Streams S6): the chat plays sandbox worker's check-in on OpenVibe.Live, `POST /api/streams/agent/:id/plays/worker`, owned by Live and internal. Only a first-party service token whose principal holds it may register a worker, and the token names the person it works for in `X-OV-Subject`, who must own the stream. The owner's own session, a desk ticket (`wpt_`) and a person token are refused: an owner who could register a worker would declare their own unisolated machine a sandbox. The worker reports the isolation facts Live requires (no host network, no credentials, no profile, egress through the proxy, an ephemeral workspace) and Live derives health only from a check-in within the last minute; Live stores no endpoint and never calls the worker. Name the capability in Live's service manifest.@@@ -28,6 +28,27 @@"inputSchema","outputSchema"]++ },++ {++ "id": "blog.review.ai_editor",++ "missing": [++ "inputSchema",++ "outputSchema"++ ]++ },++ {++ "id": "news.review.ai_editor",++ "missing": [++ "inputSchema",++ "outputSchema"++ ]++ },++ {++ "id": "wiki.review.ai_editor",++ "missing": [++ "inputSchema",++ "outputSchema"++ ]}]}@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Actor API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bench API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Billing API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Blog API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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"@@@ -1075,6 +1075,55 @@}}},++ "/api/v1/posts/{id}/reviews": {++ "post": {++ "operationId": "post_api_v1_posts_id_reviews",++ "summary": "Record the editorial queue's AI editor decision (approved or rejected) on a post revision, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject).",++ "description": "**blog.review.ai_editor** (first-party): Record the editorial queue's AI editor decision (approved or rejected) on a post revision, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets an AI draft publish through the existing publish action and the post says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.",++ "tags": [++ "blog.review"++ ],++ "x-openvibe-capabilities": [++ "blog.review.ai_editor"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "blog-write",++ "security": [++ {++ "openvibeToken": [++ "blog.review.ai_editor"++ ]++ }++ ],++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/api/v1/posts/{id}/revisions": {"get": {"operationId": "get_api_v1_posts_id_revisions",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bot API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Chat API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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"@@@ -5110,45 +5110,96 @@}}},-- "/internal/live/calls": {-- "post": {-- "operationId": "post_internal_live_calls",-- "summary": "Send a chat message to a room the actor is a member of (browsers over the chat socket; first-party services over Chat's service API).",-- "description": "**chat.message.send** (first-party): Send a chat message to a room the actor is a member of (browsers over the chat socket; first-party services over Chat's service API).",++ "/internal/config": {++ "get": {++ "operationId": "get_internal_config",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",++ "description": "**chat.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.","tags": [-- "chat.message"++ "chat.config"],"x-openvibe-capabilities": [-- "chat.message.send"++ "chat.config.manage"],"x-openvibe-visibility": [-- "first-party"++ "internal"],-- "x-openvibe-quota-class": "chat-send",++ "x-openvibe-quota-class": "config-manage","security": [{"openvibeToken": [-- "chat.message.send"++ "chat.config.manage"]}],-- "requestBody": {-- "required": false,-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/chat.send-request.v1"++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."}-- }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."}-- },++ ],"responses": {"2XX": {"description": "Success","content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/chat.send-result.v1"++ "$ref": "#/components/schemas/common.config-namespace.v1"}}}@@@ -5166,36 +5217,95 @@}}},-- "/internal/moderation/channels/{channelId}": {++ "/internal/config/{namespace}": {"get": {-- "operationId": "get_internal_moderation_channels_channelId",-- "summary": "Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.C",-- "description": "**chat.moderation.read** (internal): Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.Chat owns (plan T3). Loopback only, service token.",++ "operationId": "get_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",++ "description": "**chat.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.","tags": [-- "chat.moderation"++ "chat.config"],"x-openvibe-capabilities": [-- "chat.moderation.read"++ "chat.config.manage"],"x-openvibe-visibility": ["internal"],-- "x-openvibe-quota-class": "internal-read",++ "x-openvibe-quota-class": "config-manage","security": [{"openvibeToken": [-- "chat.moderation.read"++ "chat.config.manage"]}],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },"parameters": [{-- "name": "channelId",++ "name": "namespace","in": "path","required": true,"schema": {"type": "string"}++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."}],"responses": {@@@ -5204,7 +5314,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/chat.channel-moderation-result.v1"++ "$ref": "#/components/schemas/common.config-namespace.v1"}}}@@@ -5220,33 +5330,41 @@}}}-- }-- },-- "/internal/moderation/channels/{channelId}/emote-count": {-- "get": {-- "operationId": "get_internal_moderation_channels_channelId_emote_count",-- "summary": "Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.C",-- "description": "**chat.moderation.read** (internal): Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.Chat owns (plan T3). Loopback only, service token.",++ },++ "post": {++ "operationId": "post_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",++ "description": "**chat.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.","tags": [-- "chat.moderation"++ "chat.config"],"x-openvibe-capabilities": [-- "chat.moderation.read"++ "chat.config.manage"],"x-openvibe-visibility": ["internal"],-- "x-openvibe-quota-class": "internal-read",++ "x-openvibe-quota-class": "config-manage","security": [{"openvibeToken": [-- "chat.moderation.read"++ "chat.config.manage"]}],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },"parameters": [{-- "name": "channelId",++ "name": "namespace","in": "path","required": true,"schema": {@@@ -5260,7 +5378,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/chat.channel-moderation-result.v1"++ "$ref": "#/components/schemas/common.config-namespace.v1"}}}@@@ -5278,36 +5396,103 @@}}},-- "/internal/moderation/users/{userId}/channels": {-- "get": {-- "operationId": "get_internal_moderation_users_userId_channels",-- "summary": "Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.C",-- "description": "**chat.moderation.read** (internal): Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.Chat owns (plan T3). Loopback only, service token.",++ "/internal/config/{namespace}/secrets/{key}": {++ "delete": {++ "operationId": "delete_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",++ "description": "**chat.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.","tags": [-- "chat.moderation"++ "chat.config"],"x-openvibe-capabilities": [-- "chat.moderation.read"++ "chat.config.manage"],"x-openvibe-visibility": ["internal"],-- "x-openvibe-quota-class": "internal-read",++ "x-openvibe-quota-class": "config-manage","security": [{"openvibeToken": [-- "chat.moderation.read"++ "chat.config.manage"]}],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },"parameters": [{-- "name": "userId",++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key","in": "path","required": true,"schema": {"type": "string"}++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."}],"responses": {@@@ -5316,7 +5501,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/chat.channel-moderation-result.v1"++ "$ref": "#/components/schemas/common.config-namespace.v1"}}}@@@ -5332,50 +5517,420 @@}}}-- }-- }-- },-- "components": {-- "schemas": {-- "chat.agent-digest.v1": {-- "title": "ChatAgentDigest",-- "description": "chat.agent-digest@1 (the Agent Streams plan, S5): what a chat room said to an agent between two of its steps, GET /api/v1/rooms/agent:<id>/digest?since= on OpenVibe.Chat. The agent never reads the raw room: it asks for a digest and gets at most ten items and 1,500 characters. Each item names who (the display name and the handle), what (cut to 200 characters), its kind (a question, a suggestion or a reaction) and its upvotes (a thumbs up on the message counts). Chat chooses what goes in: repeats are grouped, greetings and spam are dropped, and the most-upvoted questions and suggestions come first. Nobody is left out by accident: a person who is timed out or banned, whose account is younger than a day, or who set 'Agents don't read my messages' is never in the digest. A link is kept as its domain only ('a link to github.com'), because the agent never follows a link from chat. The code and the commands stay as text: the agent's own tools and approvals decide what may run, never chat.",-- "type": "object",-- "additionalProperties": false,-- "required": [-- "room",-- "since",-- "until",-- "items"++ },++ "put": {++ "operationId": "put_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",++ "description": "**chat.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "chat.config"],-- "properties": {-- "room": {-- "type": "string",-- "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "The room the digest came from: an agent stream's room, `agent:<ags_id>`."-- },-- "since": {-- "type": [-- "string",-- "null"-- ],-- "format": "date-time",-- "description": "The start of the window: the moment the agent last read, or null for the first read."-- },-- "until": {-- "type": "string",-- "format": "date-time",-- "description": "The end of the window: the moment this digest was made."++ "x-openvibe-capabilities": [++ "chat.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "chat.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }},-- "items": {-- "type": "array",-- "maxItems": 10,-- "description": "What chat said, at most ten items. The order is the one Chat chose: the most-upvoted questions and suggestions first, the reactions last.",-- "items": {-- "type": "object",-- "additionalProperties": false,-- "required": [-- "who",++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/test/{key}": {++ "post": {++ "operationId": "post_internal_config_namespace_test_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",++ "description": "**chat.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "chat.config"++ ],++ "x-openvibe-capabilities": [++ "chat.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "chat.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/live/calls": {++ "post": {++ "operationId": "post_internal_live_calls",++ "summary": "Send a chat message to a room the actor is a member of (browsers over the chat socket; first-party services over Chat's service API).",++ "description": "**chat.message.send** (first-party): Send a chat message to a room the actor is a member of (browsers over the chat socket; first-party services over Chat's service API).",++ "tags": [++ "chat.message"++ ],++ "x-openvibe-capabilities": [++ "chat.message.send"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "chat-send",++ "security": [++ {++ "openvibeToken": [++ "chat.message.send"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/chat.send-request.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/chat.send-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/moderation/channels/{channelId}": {++ "get": {++ "operationId": "get_internal_moderation_channels_channelId",++ "summary": "Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.C",++ "description": "**chat.moderation.read** (internal): Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.Chat owns (plan T3). Loopback only, service token.",++ "tags": [++ "chat.moderation"++ ],++ "x-openvibe-capabilities": [++ "chat.moderation.read"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "internal-read",++ "security": [++ {++ "openvibeToken": [++ "chat.moderation.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "channelId",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/chat.channel-moderation-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/moderation/channels/{channelId}/emote-count": {++ "get": {++ "operationId": "get_internal_moderation_channels_channelId_emote_count",++ "summary": "Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.C",++ "description": "**chat.moderation.read** (internal): Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.Chat owns (plan T3). Loopback only, service token.",++ "tags": [++ "chat.moderation"++ ],++ "x-openvibe-capabilities": [++ "chat.moderation.read"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "internal-read",++ "security": [++ {++ "openvibeToken": [++ "chat.moderation.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "channelId",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/chat.channel-moderation-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/moderation/users/{userId}/channels": {++ "get": {++ "operationId": "get_internal_moderation_users_userId_channels",++ "summary": "Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.C",++ "description": "**chat.moderation.read** (internal): Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.Chat owns (plan T3). Loopback only, service token.",++ "tags": [++ "chat.moderation"++ ],++ "x-openvibe-capabilities": [++ "chat.moderation.read"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "internal-read",++ "security": [++ {++ "openvibeToken": [++ "chat.moderation.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "userId",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/chat.channel-moderation-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ }++ },++ "components": {++ "schemas": {++ "chat.agent-digest.v1": {++ "title": "ChatAgentDigest",++ "description": "chat.agent-digest@1 (the Agent Streams plan, S5): what a chat room said to an agent between two of its steps, GET /api/v1/rooms/agent:<id>/digest?since= on OpenVibe.Chat. The agent never reads the raw room: it asks for a digest and gets at most ten items and 1,500 characters. Each item names who (the display name and the handle), what (cut to 200 characters), its kind (a question, a suggestion or a reaction) and its upvotes (a thumbs up on the message counts). Chat chooses what goes in: repeats are grouped, greetings and spam are dropped, and the most-upvoted questions and suggestions come first. Nobody is left out by accident: a person who is timed out or banned, whose account is younger than a day, or who set 'Agents don't read my messages' is never in the digest. A link is kept as its domain only ('a link to github.com'), because the agent never follows a link from chat. The code and the commands stay as text: the agent's own tools and approvals decide what may run, never chat.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "room",++ "since",++ "until",++ "items"++ ],++ "properties": {++ "room": {++ "type": "string",++ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The room the digest came from: an agent stream's room, `agent:<ags_id>`."++ },++ "since": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "The start of the window: the moment the agent last read, or null for the first read."++ },++ "until": {++ "type": "string",++ "format": "date-time",++ "description": "The end of the window: the moment this digest was made."++ },++ "items": {++ "type": "array",++ "maxItems": 10,++ "description": "What chat said, at most ten items. The order is the one Chat chose: the most-upvoted questions and suggestions first, the reactions last.",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "who","what","kind","upvotes"@@@ -9817,164 +10372,591 @@"maxLength": 2000,"description": "What they asked."},-- "source": {++ "source": {++ "type": "object",++ "required": [++ "kind",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "kind": {++ "enum": [++ "voice"++ ]++ },++ "id": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_.:-]{1,64}$",++ "description": "The source's stable id (the machine's device id): one chat per source."++ },++ "name": {++ "type": "string",++ "maxLength": 80,++ "description": "What to call it in the chat's name (the machine's name)."++ }++ }++ },++ "lang": {++ "type": "string",++ "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8}){0,2}$"++ },++ "idempotency_key": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_.:-]{1,128}$",++ "description": "The caller's id for this ask (Bot: device id and the voice_ask id): asked again, the same answer, once."++ }++ }++ },++ "chat.vibe-ask-result.v1": {++ "title": "ChatVibeAskResult",++ "description": "chat.vibe-ask-result@1: Vibe's answer to a chat.vibe-ask-request@1. `answered` carries the reply (plain text, Markdown stripped, for speech); `refused` says why nothing was asked (Vibe resting, the person's daily allowance spent). `url` is the Vibe chat that keeps the question and the answer.",++ "type": "object",++ "required": [++ "state",++ "text"++ ],++ "additionalProperties": false,++ "properties": {++ "state": {++ "enum": [++ "answered",++ "refused"++ ]++ },++ "text": {++ "type": "string",++ "maxLength": 4000++ },++ "url": {++ "type": "string",++ "format": "uri",++ "pattern": "^https://"++ },++ "chat": {++ "type": "string",++ "pattern": "^v-[0-9a-f]{12}$",++ "description": "The Vibe chat's slug."++ },++ "code": {++ "type": "string",++ "description": "Why it was refused (chat.vibe.off, chat.vibe.allowance)."++ }++ }++ },++ "chat.vibe-deliver-request.v1": {++ "title": "ChatVibeDeliverRequest",++ "description": "chat.vibe-deliver-request@1: a first-party service puts a result of the person's own work into their Vibe chats (OpenVibe.Actor: each finished run of one of their OpenVibe Agents, when the agent sends its results to Vibe). Chat keeps one chat per source, named after it, and posts the text there as Vibe's message, so each run is a chat they can open and continue. No AI answer is made and nothing counts against their allowance. POST /internal/chat/vibe/deliver, capability chat.vibe.deliver, loopback only.",++ "type": "object",++ "required": [++ "subject",++ "source",++ "text",++ "idempotency_key"++ ],++ "additionalProperties": false,++ "properties": {++ "subject": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The person whose work it is, as the calling service vouches (Actor: the agent's owner)."++ },++ "source": {++ "type": "object",++ "required": [++ "kind",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "kind": {++ "enum": [++ "agent"++ ]++ },++ "id": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_.:-]{1,64}$",++ "description": "The source's stable id (the agent's agt_ id): one chat per source."++ },++ "name": {++ "type": "string",++ "maxLength": 80,++ "description": "What to call it in the chat's name (the agent's name)."++ }++ }++ },++ "text": {++ "type": "string",++ "minLength": 1,++ "maxLength": 8000,++ "description": "The result, in Markdown (as Vibe's answers are shown)."++ },++ "url": {++ "type": "string",++ "format": "uri",++ "pattern": "^https://",++ "description": "Where the whole run is (its steps, sources and cost)."++ },++ "idempotency_key": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_.:-]{1,128}$",++ "description": "The caller's id for this result (Actor: the run's task id): sent again, it is posted once."++ }++ }++ },++ "chat.vibe-deliver-result.v1": {++ "title": "ChatVibeDeliverResult",++ "description": "chat.vibe-deliver-result@1: where a chat.vibe-deliver-request@1 was posted. `repeated` is true when the idempotency key was seen before (nothing was posted again).",++ "type": "object",++ "required": [++ "chat",++ "url",++ "repeated"++ ],++ "additionalProperties": false,++ "properties": {++ "chat": {++ "type": "string",++ "pattern": "^v-[0-9a-f]{12}$",++ "description": "The Vibe chat's slug."++ },++ "url": {++ "type": "string",++ "format": "uri",++ "pattern": "^https://"++ },++ "repeated": {++ "type": "boolean"++ }++ }++ },++ "common.config-change.v1": {++ "title": "ConfigChange",++ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "values",++ "reason"++ ],++ "properties": {++ "values": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "reason": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "merge": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "unset": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ }++ }++ },++ "common.config-namespace.v1": {++ "title": "ConfigNamespace",++ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "service",++ "namespace",++ "revision",++ "values",++ "classification",++ "sealed",++ "pins",++ "schema"++ ],++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."++ },++ "revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The active revision; null before the namespace has one."++ },++ "values": {++ "type": "object",++ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "sealed": {++ "type": "boolean",++ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."++ },++ "pins": {++ "type": "array",++ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "schema": {++ "type": [++ "object",++ "null"++ ],++ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."++ },++ "active": {++ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "last_known_good": {++ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ }++ },++ "$defs": {++ "marker": {"type": "object",++ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",++ "additionalProperties": false,"required": [-- "kind",-- "id"++ "redacted",++ "fingerprint"],-- "additionalProperties": false,"properties": {-- "kind": {-- "enum": [-- "voice"-- ]++ "redacted": {++ "const": true},-- "id": {++ "fingerprint": {"type": "string",-- "pattern": "^[A-Za-z0-9_.:-]{1,64}$",-- "description": "The source's stable id (the machine's device id): one chat per source."++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."},-- "name": {++ "hint": {"type": "string",-- "maxLength": 80,-- "description": "What to call it in the chat's name (the machine's name)."++ "minLength": 4,++ "maxLength": 4,++ "description": "The four-character hint (the value's last four characters), never more."++ },++ "set_at": {++ "type": "string",++ "format": "date-time"++ },++ "set_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "bound_to": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2048,++ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."}}-- },-- "lang": {-- "type": "string",-- "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8}){0,2}$"-- },-- "idempotency_key": {-- "type": "string",-- "pattern": "^[A-Za-z0-9_.:-]{1,128}$",-- "description": "The caller's id for this ask (Bot: device id and the voice_ask id): asked again, the same answer, once."}}},-- "chat.vibe-ask-result.v1": {-- "title": "ChatVibeAskResult",-- "description": "chat.vibe-ask-result@1: Vibe's answer to a chat.vibe-ask-request@1. `answered` carries the reply (plain text, Markdown stripped, for speech); `refused` says why nothing was asked (Vibe resting, the person's daily allowance spent). `url` is the Vibe chat that keeps the question and the answer.",++ "common.config-snapshot.v1": {++ "title": "ConfigSnapshot",++ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.","type": "object","required": [++ "service",++ "namespace",++ "revision",++ "previous_revision","state",-- "text"++ "values",++ "classification",++ "checksum",++ "created_at",++ "created_by"],"additionalProperties": false,"properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "previous_revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The revision that was active when this one was proposed; null for the namespace's first."++ },"state": {"enum": [-- "answered",-- "refused"++ "proposed",++ "active",++ "superseded",++ "rejected",++ "rolled_back"]},-- "text": {-- "type": "string",-- "maxLength": 4000++ "values": {++ "type": "object",++ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "type": "object",++ "required": [++ "redacted",++ "fingerprint"++ ],++ "additionalProperties": false,++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."++ }++ },++ "description": "A redacted secret-class value."++ }++ ]++ }},-- "url": {-- "type": "string",-- "format": "uri",-- "pattern": "^https://"++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }},-- "chat": {++ "checksum": {"type": "string",-- "pattern": "^v-[0-9a-f]{12}$",-- "description": "The Vibe chat's slug."++ "pattern": "^[0-9a-f]{64}$",++ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."},-- "code": {-- "type": "string",-- "description": "Why it was refused (chat.vibe.off, chat.vibe.allowance)."-- }-- }-- },-- "chat.vibe-deliver-request.v1": {-- "title": "ChatVibeDeliverRequest",-- "description": "chat.vibe-deliver-request@1: a first-party service puts a result of the person's own work into their Vibe chats (OpenVibe.Actor: each finished run of one of their OpenVibe Agents, when the agent sends its results to Vibe). Chat keeps one chat per source, named after it, and posts the text there as Vibe's message, so each run is a chat they can open and continue. No AI answer is made and nothing counts against their allowance. POST /internal/chat/vibe/deliver, capability chat.vibe.deliver, loopback only.",-- "type": "object",-- "required": [-- "subject",-- "source",-- "text",-- "idempotency_key"-- ],-- "additionalProperties": false,-- "properties": {-- "subject": {++ "created_at": {"type": "string",-- "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "The person whose work it is, as the calling service vouches (Actor: the agent's owner)."++ "format": "date-time"},-- "source": {-- "type": "object",-- "required": [-- "kind",-- "id"++ "created_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "activated_at": {++ "type": [++ "string",++ "null"],-- "additionalProperties": false,-- "properties": {-- "kind": {-- "enum": [-- "agent"-- ]-- },-- "id": {-- "type": "string",-- "pattern": "^[A-Za-z0-9_.:-]{1,64}$",-- "description": "The source's stable id (the agent's agt_ id): one chat per source."++ "format": "date-time",++ "description": "When it became active; null while proposed, and for a rejected revision."++ },++ "activated_by": {++ "oneOf": [++ {++ "$ref": "#/components/schemas/identity.subject-ref.v1"},-- "name": {-- "type": "string",-- "maxLength": 80,-- "description": "What to call it in the chat's name (the agent's name)."++ {++ "type": "null"}-- }++ ]},-- "text": {-- "type": "string",-- "minLength": 1,-- "maxLength": 8000,-- "description": "The result, in Markdown (as Vibe's answers are shown)."++ "reason": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500},-- "url": {-- "type": "string",-- "format": "uri",-- "pattern": "^https://",-- "description": "Where the whole run is (its steps, sources and cost)."++ "error": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2000,++ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."},-- "idempotency_key": {-- "type": "string",-- "pattern": "^[A-Za-z0-9_.:-]{1,128}$",-- "description": "The caller's id for this result (Actor: the run's task id): sent again, it is posted once."++ "copied_from": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "A rollback's source: the revision whose values this one copies."}-- }-- },-- "chat.vibe-deliver-result.v1": {-- "title": "ChatVibeDeliverResult",-- "description": "chat.vibe-deliver-result@1: where a chat.vibe-deliver-request@1 was posted. `repeated` is true when the idempotency key was seen before (nothing was posted again).",-- "type": "object",-- "required": [-- "chat",-- "url",-- "repeated"-- ],-- "additionalProperties": false,-- "properties": {-- "chat": {-- "type": "string",-- "pattern": "^v-[0-9a-f]{12}$",-- "description": "The Vibe chat's slug."-- },-- "url": {-- "type": "string",-- "format": "uri",-- "pattern": "^https://"++ },++ "allOf": [++ {++ "if": {++ "properties": {++ "state": {++ "const": "rejected"++ }++ }++ },++ "then": {++ "required": [++ "error"++ ],++ "properties": {++ "error": {++ "type": "string",++ "minLength": 1++ }++ }++ }},-- "repeated": {-- "type": "boolean"++ {++ "if": {++ "properties": {++ "state": {++ "enum": [++ "active",++ "superseded",++ "rolled_back"++ ]++ }++ }++ },++ "then": {++ "required": [++ "activated_at"++ ],++ "properties": {++ "activated_at": {++ "type": "string"++ }++ }++ }}-- }++ ]},"common.owned-resource-control-request.v1": {"title": "OwnedResourceControlRequest",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Codes API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Community API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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"nnew file mode 100644@@@ -0,0 +1,1197 @@++{++ "openapi": "3.1.0",++ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",++ "info": {++ "title": "OpenVibe Connect API",++ "version": "0.315.0",++ "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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://connect.openvibe.website"++ }++ ],++ "paths": {++ "/internal/config": {++ "get": {++ "operationId": "get_internal_config",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**connect.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "connect.config"++ ],++ "x-openvibe-capabilities": [++ "connect.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "connect.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}": {++ "get": {++ "operationId": "get_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**connect.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "connect.config"++ ],++ "x-openvibe-capabilities": [++ "connect.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "connect.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**connect.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "connect.config"++ ],++ "x-openvibe-capabilities": [++ "connect.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "connect.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/secrets/{key}": {++ "delete": {++ "operationId": "delete_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**connect.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "connect.config"++ ],++ "x-openvibe-capabilities": [++ "connect.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "connect.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**connect.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "connect.config"++ ],++ "x-openvibe-capabilities": [++ "connect.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "connect.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/test/{key}": {++ "post": {++ "operationId": "post_internal_config_namespace_test_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**connect.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "connect.config"++ ],++ "x-openvibe-capabilities": [++ "connect.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "connect.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ }++ },++ "components": {++ "schemas": {++ "common.config-change.v1": {++ "title": "ConfigChange",++ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "values",++ "reason"++ ],++ "properties": {++ "values": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "reason": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "merge": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "unset": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ }++ }++ },++ "common.config-namespace.v1": {++ "title": "ConfigNamespace",++ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "service",++ "namespace",++ "revision",++ "values",++ "classification",++ "sealed",++ "pins",++ "schema"++ ],++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."++ },++ "revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The active revision; null before the namespace has one."++ },++ "values": {++ "type": "object",++ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "sealed": {++ "type": "boolean",++ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."++ },++ "pins": {++ "type": "array",++ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "schema": {++ "type": [++ "object",++ "null"++ ],++ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."++ },++ "active": {++ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "last_known_good": {++ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ }++ },++ "$defs": {++ "marker": {++ "type": "object",++ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",++ "additionalProperties": false,++ "required": [++ "redacted",++ "fingerprint"++ ],++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."++ },++ "hint": {++ "type": "string",++ "minLength": 4,++ "maxLength": 4,++ "description": "The four-character hint (the value's last four characters), never more."++ },++ "set_at": {++ "type": "string",++ "format": "date-time"++ },++ "set_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "bound_to": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2048,++ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."++ }++ }++ }++ }++ },++ "common.config-snapshot.v1": {++ "title": "ConfigSnapshot",++ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",++ "type": "object",++ "required": [++ "service",++ "namespace",++ "revision",++ "previous_revision",++ "state",++ "values",++ "classification",++ "checksum",++ "created_at",++ "created_by"++ ],++ "additionalProperties": false,++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "previous_revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The revision that was active when this one was proposed; null for the namespace's first."++ },++ "state": {++ "enum": [++ "proposed",++ "active",++ "superseded",++ "rejected",++ "rolled_back"++ ]++ },++ "values": {++ "type": "object",++ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "type": "object",++ "required": [++ "redacted",++ "fingerprint"++ ],++ "additionalProperties": false,++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."++ }++ },++ "description": "A redacted secret-class value."++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "checksum": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "created_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "activated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "When it became active; null while proposed, and for a rejected revision."++ },++ "activated_by": {++ "oneOf": [++ {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "reason": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500++ },++ "error": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2000,++ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."++ },++ "copied_from": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "A rollback's source: the revision whose values this one copies."++ }++ },++ "allOf": [++ {++ "if": {++ "properties": {++ "state": {++ "const": "rejected"++ }++ }++ },++ "then": {++ "required": [++ "error"++ ],++ "properties": {++ "error": {++ "type": "string",++ "minLength": 1++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "state": {++ "enum": [++ "active",++ "superseded",++ "rolled_back"++ ]++ }++ }++ },++ "then": {++ "required": [++ "activated_at"++ ],++ "properties": {++ "activated_at": {++ "type": "string"++ }++ }++ }++ }++ ]++ },++ "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}."++ }++ }++ },++ "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": {++ "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."++ }++ }++ }++}@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Coupons API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Deals API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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"@@@ -1112,10 +1112,992 @@}}}++ },++ "/internal/config": {++ "get": {++ "operationId": "get_internal_config",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists t",++ "description": "**deals.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "deals.config"++ ],++ "x-openvibe-capabilities": [++ "deals.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "deals.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}": {++ "get": {++ "operationId": "get_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists t",++ "description": "**deals.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "deals.config"++ ],++ "x-openvibe-capabilities": [++ "deals.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "deals.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists t",++ "description": "**deals.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "deals.config"++ ],++ "x-openvibe-capabilities": [++ "deals.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "deals.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/secrets/{key}": {++ "delete": {++ "operationId": "delete_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists t",++ "description": "**deals.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "deals.config"++ ],++ "x-openvibe-capabilities": [++ "deals.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "deals.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists t",++ "description": "**deals.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "deals.config"++ ],++ "x-openvibe-capabilities": [++ "deals.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "deals.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/test/{key}": {++ "post": {++ "operationId": "post_internal_config_namespace_test_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists t",++ "description": "**deals.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "deals.config"++ ],++ "x-openvibe-capabilities": [++ "deals.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "deals.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }}},"components": {"schemas": {++ "common.config-change.v1": {++ "title": "ConfigChange",++ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "values",++ "reason"++ ],++ "properties": {++ "values": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "reason": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "merge": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "unset": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ }++ }++ },++ "common.config-namespace.v1": {++ "title": "ConfigNamespace",++ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "service",++ "namespace",++ "revision",++ "values",++ "classification",++ "sealed",++ "pins",++ "schema"++ ],++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."++ },++ "revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The active revision; null before the namespace has one."++ },++ "values": {++ "type": "object",++ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "sealed": {++ "type": "boolean",++ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."++ },++ "pins": {++ "type": "array",++ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "schema": {++ "type": [++ "object",++ "null"++ ],++ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."++ },++ "active": {++ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "last_known_good": {++ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ }++ },++ "$defs": {++ "marker": {++ "type": "object",++ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",++ "additionalProperties": false,++ "required": [++ "redacted",++ "fingerprint"++ ],++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."++ },++ "hint": {++ "type": "string",++ "minLength": 4,++ "maxLength": 4,++ "description": "The four-character hint (the value's last four characters), never more."++ },++ "set_at": {++ "type": "string",++ "format": "date-time"++ },++ "set_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "bound_to": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2048,++ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."++ }++ }++ }++ }++ },++ "common.config-snapshot.v1": {++ "title": "ConfigSnapshot",++ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",++ "type": "object",++ "required": [++ "service",++ "namespace",++ "revision",++ "previous_revision",++ "state",++ "values",++ "classification",++ "checksum",++ "created_at",++ "created_by"++ ],++ "additionalProperties": false,++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "previous_revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The revision that was active when this one was proposed; null for the namespace's first."++ },++ "state": {++ "enum": [++ "proposed",++ "active",++ "superseded",++ "rejected",++ "rolled_back"++ ]++ },++ "values": {++ "type": "object",++ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "type": "object",++ "required": [++ "redacted",++ "fingerprint"++ ],++ "additionalProperties": false,++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."++ }++ },++ "description": "A redacted secret-class value."++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "checksum": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "created_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "activated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "When it became active; null while proposed, and for a rejected revision."++ },++ "activated_by": {++ "oneOf": [++ {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "reason": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500++ },++ "error": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2000,++ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."++ },++ "copied_from": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "A rollback's source: the revision whose values this one copies."++ }++ },++ "allOf": [++ {++ "if": {++ "properties": {++ "state": {++ "const": "rejected"++ }++ }++ },++ "then": {++ "required": [++ "error"++ ],++ "properties": {++ "error": {++ "type": "string",++ "minLength": 1++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "state": {++ "enum": [++ "active",++ "superseded",++ "rolled_back"++ ]++ }++ }++ },++ "then": {++ "required": [++ "activated_at"++ ],++ "properties": {++ "activated_at": {++ "type": "string"++ }++ }++ }++ }++ ]++ },"common.no-body.v1": {"title": "NoBody","description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",@@@ -3001,6 +3983,123 @@"description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."}}++ },++ "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": {nnew file mode 100644@@@ -0,0 +1,1197 @@++{++ "openapi": "3.1.0",++ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",++ "info": {++ "title": "OpenVibe.Design API",++ "version": "0.315.0",++ "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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.design"++ }++ ],++ "paths": {++ "/internal/config": {++ "get": {++ "operationId": "get_internal_config",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists ",++ "description": "**design.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "design.config"++ ],++ "x-openvibe-capabilities": [++ "design.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "design.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}": {++ "get": {++ "operationId": "get_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists ",++ "description": "**design.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "design.config"++ ],++ "x-openvibe-capabilities": [++ "design.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "design.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists ",++ "description": "**design.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "design.config"++ ],++ "x-openvibe-capabilities": [++ "design.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "design.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/secrets/{key}": {++ "delete": {++ "operationId": "delete_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists ",++ "description": "**design.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "design.config"++ ],++ "x-openvibe-capabilities": [++ "design.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "design.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists ",++ "description": "**design.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "design.config"++ ],++ "x-openvibe-capabilities": [++ "design.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "design.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/test/{key}": {++ "post": {++ "operationId": "post_internal_config_namespace_test_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists ",++ "description": "**design.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "design.config"++ ],++ "x-openvibe-capabilities": [++ "design.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "design.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ }++ },++ "components": {++ "schemas": {++ "common.config-change.v1": {++ "title": "ConfigChange",++ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "values",++ "reason"++ ],++ "properties": {++ "values": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "reason": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "merge": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "unset": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ }++ }++ },++ "common.config-namespace.v1": {++ "title": "ConfigNamespace",++ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "service",++ "namespace",++ "revision",++ "values",++ "classification",++ "sealed",++ "pins",++ "schema"++ ],++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."++ },++ "revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The active revision; null before the namespace has one."++ },++ "values": {++ "type": "object",++ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "sealed": {++ "type": "boolean",++ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."++ },++ "pins": {++ "type": "array",++ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "schema": {++ "type": [++ "object",++ "null"++ ],++ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."++ },++ "active": {++ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "last_known_good": {++ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ }++ },++ "$defs": {++ "marker": {++ "type": "object",++ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",++ "additionalProperties": false,++ "required": [++ "redacted",++ "fingerprint"++ ],++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."++ },++ "hint": {++ "type": "string",++ "minLength": 4,++ "maxLength": 4,++ "description": "The four-character hint (the value's last four characters), never more."++ },++ "set_at": {++ "type": "string",++ "format": "date-time"++ },++ "set_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "bound_to": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2048,++ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."++ }++ }++ }++ }++ },++ "common.config-snapshot.v1": {++ "title": "ConfigSnapshot",++ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",++ "type": "object",++ "required": [++ "service",++ "namespace",++ "revision",++ "previous_revision",++ "state",++ "values",++ "classification",++ "checksum",++ "created_at",++ "created_by"++ ],++ "additionalProperties": false,++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "previous_revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The revision that was active when this one was proposed; null for the namespace's first."++ },++ "state": {++ "enum": [++ "proposed",++ "active",++ "superseded",++ "rejected",++ "rolled_back"++ ]++ },++ "values": {++ "type": "object",++ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "type": "object",++ "required": [++ "redacted",++ "fingerprint"++ ],++ "additionalProperties": false,++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."++ }++ },++ "description": "A redacted secret-class value."++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "checksum": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "created_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "activated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "When it became active; null while proposed, and for a rejected revision."++ },++ "activated_by": {++ "oneOf": [++ {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "reason": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500++ },++ "error": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2000,++ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."++ },++ "copied_from": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "A rollback's source: the revision whose values this one copies."++ }++ },++ "allOf": [++ {++ "if": {++ "properties": {++ "state": {++ "const": "rejected"++ }++ }++ },++ "then": {++ "required": [++ "error"++ ],++ "properties": {++ "error": {++ "type": "string",++ "minLength": 1++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "state": {++ "enum": [++ "active",++ "superseded",++ "rolled_back"++ ]++ }++ }++ },++ "then": {++ "required": [++ "activated_at"++ ],++ "properties": {++ "activated_at": {++ "type": "string"++ }++ }++ }++ }++ ]++ },++ "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}."++ }++ }++ },++ "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": {++ "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."++ }++ }++ }++}@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Domains API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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"@@@ -117,6 +117,561 @@}}}++ },++ "/internal/config": {++ "get": {++ "operationId": "get_internal_config",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**domains.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "domains.config"++ ],++ "x-openvibe-capabilities": [++ "domains.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "domains.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}": {++ "get": {++ "operationId": "get_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**domains.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "domains.config"++ ],++ "x-openvibe-capabilities": [++ "domains.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "domains.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**domains.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "domains.config"++ ],++ "x-openvibe-capabilities": [++ "domains.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "domains.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/secrets/{key}": {++ "delete": {++ "operationId": "delete_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**domains.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "domains.config"++ ],++ "x-openvibe-capabilities": [++ "domains.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "domains.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**domains.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "domains.config"++ ],++ "x-openvibe-capabilities": [++ "domains.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "domains.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/test/{key}": {++ "post": {++ "operationId": "post_internal_config_namespace_test_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**domains.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "domains.config"++ ],++ "x-openvibe-capabilities": [++ "domains.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "domains.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }}},"components": {@@@ -285,6 +840,433 @@}}},++ "common.config-change.v1": {++ "title": "ConfigChange",++ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "values",++ "reason"++ ],++ "properties": {++ "values": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "reason": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "merge": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "unset": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ }++ }++ },++ "common.config-namespace.v1": {++ "title": "ConfigNamespace",++ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "service",++ "namespace",++ "revision",++ "values",++ "classification",++ "sealed",++ "pins",++ "schema"++ ],++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."++ },++ "revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The active revision; null before the namespace has one."++ },++ "values": {++ "type": "object",++ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "sealed": {++ "type": "boolean",++ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."++ },++ "pins": {++ "type": "array",++ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "schema": {++ "type": [++ "object",++ "null"++ ],++ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."++ },++ "active": {++ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "last_known_good": {++ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ }++ },++ "$defs": {++ "marker": {++ "type": "object",++ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",++ "additionalProperties": false,++ "required": [++ "redacted",++ "fingerprint"++ ],++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."++ },++ "hint": {++ "type": "string",++ "minLength": 4,++ "maxLength": 4,++ "description": "The four-character hint (the value's last four characters), never more."++ },++ "set_at": {++ "type": "string",++ "format": "date-time"++ },++ "set_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "bound_to": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2048,++ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."++ }++ }++ }++ }++ },++ "common.config-snapshot.v1": {++ "title": "ConfigSnapshot",++ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",++ "type": "object",++ "required": [++ "service",++ "namespace",++ "revision",++ "previous_revision",++ "state",++ "values",++ "classification",++ "checksum",++ "created_at",++ "created_by"++ ],++ "additionalProperties": false,++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "previous_revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The revision that was active when this one was proposed; null for the namespace's first."++ },++ "state": {++ "enum": [++ "proposed",++ "active",++ "superseded",++ "rejected",++ "rolled_back"++ ]++ },++ "values": {++ "type": "object",++ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "type": "object",++ "required": [++ "redacted",++ "fingerprint"++ ],++ "additionalProperties": false,++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."++ }++ },++ "description": "A redacted secret-class value."++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "checksum": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "created_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "activated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "When it became active; null while proposed, and for a rejected revision."++ },++ "activated_by": {++ "oneOf": [++ {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "reason": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500++ },++ "error": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2000,++ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."++ },++ "copied_from": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "A rollback's source: the revision whose values this one copies."++ }++ },++ "allOf": [++ {++ "if": {++ "properties": {++ "state": {++ "const": "rejected"++ }++ }++ },++ "then": {++ "required": [++ "error"++ ],++ "properties": {++ "error": {++ "type": "string",++ "minLength": 1++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "state": {++ "enum": [++ "active",++ "superseded",++ "rolled_back"++ ]++ }++ }++ },++ "then": {++ "required": [++ "activated_at"++ ],++ "properties": {++ "activated_at": {++ "type": "string"++ }++ }++ }++ }++ ]++ },"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.",@@@ -350,6 +1332,123 @@"description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."}}++ },++ "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": {@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Events API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Food API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Games API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Host API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -33,8 +33,8 @@"service": "blog","name": "OpenVibe.Blog","origin": "https://openvibe.blog",-- "operations": 22,-- "capabilities": 12++ "operations": 23,++ "capabilities": 13},{"service": "bot",@@@ -47,8 +47,8 @@"service": "chat","name": "OpenVibe.Chat","origin": "https://openvibe.chat",-- "operations": 60,-- "capabilities": 24++ "operations": 66,++ "capabilities": 25},{"service": "codes",@@@ -64,6 +64,13 @@"operations": 40,"capabilities": 13},++ {++ "service": "connect",++ "name": "OpenVibe Connect",++ "origin": "https://connect.openvibe.website",++ "operations": 6,++ "capabilities": 1++ },{"service": "coupons","name": "OpenVibe.Coupons",@@@ -75,15 +82,22 @@"service": "deals","name": "OpenVibe.Deals","origin": "https://openvibe.deals",-- "operations": 18,-- "capabilities": 12++ "operations": 24,++ "capabilities": 13++ },++ {++ "service": "design",++ "name": "OpenVibe.Design",++ "origin": "https://openvibe.design",++ "operations": 6,++ "capabilities": 1},{"service": "domains","name": "OpenVibe.Domains","origin": "https://openvibe.domains",-- "operations": 2,-- "capabilities": 2++ "operations": 8,++ "capabilities": 3},{"service": "events",@@@ -152,8 +166,8 @@"service": "news","name": "OpenVibe.News","origin": "https://openvibe.news",-- "operations": 26,-- "capabilities": 11++ "operations": 28,++ "capabilities": 12},{"service": "openre",@@@ -215,8 +229,8 @@"service": "sources","name": "OpenVibe.Sources","origin": "https://sources.openvibe.network",-- "operations": 13,-- "capabilities": 3++ "operations": 19,++ "capabilities": 4},{"service": "space",@@@ -279,14 +293,14 @@"name": "OpenVibe.Wiki","origin": "https://openvibe.wiki","operations": 25,-- "capabilities": 8++ "capabilities": 9},{"service": "work","name": "OpenVibe.Work","origin": "https://openvibe.work",-- "operations": 5,-- "capabilities": 1++ "operations": 11,++ "capabilities": 2},{"service": "zone",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Inventory API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.MediaHub API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Media API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Network API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.News API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -16,6 +16,55 @@}],"paths": {++ "/api/v1/briefs/{id}/reviews": {++ "post": {++ "operationId": "post_api_v1_briefs_id_reviews",++ "summary": "Record the editorial queue's AI editor decision (approved or rejected) on a story revision or an AI brief, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject).",++ "description": "**news.review.ai_editor** (first-party): Record the editorial queue's AI editor decision (approved or rejected) on a story revision or an AI brief, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets AI text publish and index and the page says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.",++ "tags": [++ "news.review"++ ],++ "x-openvibe-capabilities": [++ "news.review.ai_editor"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "news-write",++ "security": [++ {++ "openvibeToken": [++ "news.review.ai_editor"++ ]++ }++ ],++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/api/v1/clusters": {"get": {"operationId": "get_api_v1_clusters",@@@ -983,6 +1032,55 @@}}},++ "/api/v1/stories/{id}/reviews": {++ "post": {++ "operationId": "post_api_v1_stories_id_reviews",++ "summary": "Record the editorial queue's AI editor decision (approved or rejected) on a story revision or an AI brief, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject).",++ "description": "**news.review.ai_editor** (first-party): Record the editorial queue's AI editor decision (approved or rejected) on a story revision or an AI brief, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets AI text publish and index and the page says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.",++ "tags": [++ "news.review"++ ],++ "x-openvibe-capabilities": [++ "news.review.ai_editor"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "news-write",++ "security": [++ {++ "openvibeToken": [++ "news.review.ai_editor"++ ]++ }++ ],++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/api/v1/stories/{id}/revisions": {"get": {"operationId": "get_api_v1_stories_id_revisions",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenRestream API",-- "version": "0.314.0",-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Play API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Quest API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Report API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Reviews API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Run API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Search API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Services API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Sources API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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"@@@ -731,10 +731,992 @@}}}++ },++ "/internal/config": {++ "get": {++ "operationId": "get_internal_config",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**sources.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "sources.config"++ ],++ "x-openvibe-capabilities": [++ "sources.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "sources.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}": {++ "get": {++ "operationId": "get_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**sources.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "sources.config"++ ],++ "x-openvibe-capabilities": [++ "sources.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "sources.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**sources.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "sources.config"++ ],++ "x-openvibe-capabilities": [++ "sources.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "sources.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/secrets/{key}": {++ "delete": {++ "operationId": "delete_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**sources.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "sources.config"++ ],++ "x-openvibe-capabilities": [++ "sources.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "sources.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**sources.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "sources.config"++ ],++ "x-openvibe-capabilities": [++ "sources.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "sources.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/test/{key}": {++ "post": {++ "operationId": "post_internal_config_namespace_test_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",++ "description": "**sources.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "sources.config"++ ],++ "x-openvibe-capabilities": [++ "sources.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "sources.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }}},"components": {"schemas": {++ "common.config-change.v1": {++ "title": "ConfigChange",++ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "values",++ "reason"++ ],++ "properties": {++ "values": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "reason": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "merge": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "unset": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ }++ }++ },++ "common.config-namespace.v1": {++ "title": "ConfigNamespace",++ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "service",++ "namespace",++ "revision",++ "values",++ "classification",++ "sealed",++ "pins",++ "schema"++ ],++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."++ },++ "revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The active revision; null before the namespace has one."++ },++ "values": {++ "type": "object",++ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "sealed": {++ "type": "boolean",++ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."++ },++ "pins": {++ "type": "array",++ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "schema": {++ "type": [++ "object",++ "null"++ ],++ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."++ },++ "active": {++ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "last_known_good": {++ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ }++ },++ "$defs": {++ "marker": {++ "type": "object",++ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",++ "additionalProperties": false,++ "required": [++ "redacted",++ "fingerprint"++ ],++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."++ },++ "hint": {++ "type": "string",++ "minLength": 4,++ "maxLength": 4,++ "description": "The four-character hint (the value's last four characters), never more."++ },++ "set_at": {++ "type": "string",++ "format": "date-time"++ },++ "set_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "bound_to": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2048,++ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."++ }++ }++ }++ }++ },++ "common.config-snapshot.v1": {++ "title": "ConfigSnapshot",++ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",++ "type": "object",++ "required": [++ "service",++ "namespace",++ "revision",++ "previous_revision",++ "state",++ "values",++ "classification",++ "checksum",++ "created_at",++ "created_by"++ ],++ "additionalProperties": false,++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "previous_revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The revision that was active when this one was proposed; null for the namespace's first."++ },++ "state": {++ "enum": [++ "proposed",++ "active",++ "superseded",++ "rejected",++ "rolled_back"++ ]++ },++ "values": {++ "type": "object",++ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "type": "object",++ "required": [++ "redacted",++ "fingerprint"++ ],++ "additionalProperties": false,++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."++ }++ },++ "description": "A redacted secret-class value."++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "checksum": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "created_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "activated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "When it became active; null while proposed, and for a rejected revision."++ },++ "activated_by": {++ "oneOf": [++ {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "reason": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500++ },++ "error": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2000,++ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."++ },++ "copied_from": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "A rollback's source: the revision whose values this one copies."++ }++ },++ "allOf": [++ {++ "if": {++ "properties": {++ "state": {++ "const": "rejected"++ }++ }++ },++ "then": {++ "required": [++ "error"++ ],++ "properties": {++ "error": {++ "type": "string",++ "minLength": 1++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "state": {++ "enum": [++ "active",++ "superseded",++ "rolled_back"++ ]++ }++ }++ },++ "then": {++ "required": [++ "activated_at"++ ],++ "properties": {++ "activated_at": {++ "type": "string"++ }++ }++ }++ }++ ]++ },"common.no-body.v1": {"title": "NoBody","description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",@@@ -807,6 +1789,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."++ }++ ]++ },"sources.item-read-result.v1": {"title": "SourcesItemReadResult","description": "sources.item-read-result@1: answers of sources.item.read on OpenVibe.Sources. GET /api/v1/items?source=&category=&after=&limit=&include_removed= → { items, next_after, more, sources } (a change feed: items in change order, at most 500; sources gives each listed source's health); GET /api/v1/items/:id[?revisions=1] → { item, source } (the item, with its revisions when asked, and its source's health; null when the source is gone).",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Space API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tips API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Today API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tools API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Trade API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.VIP API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Vision API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Watch API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Wiki API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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"@@@ -836,12 +836,14 @@"/api/v1/pages/{id}/revisions/{n}/review": {"post": {"operationId": "post_api_v1_pages_id_revisions_n_review",-- "summary": "Publish a revision now or at a scheduled time (idempotent), unpublish a page, and record a person's review (approved or rejected) of an AI proposal or of an existing AI-assisted revision, on behalf of",-- "description": "**wiki.revision.publish** (first-party): Publish a revision now or at a scheduled time (idempotent), unpublish a page, and record a person's review (approved or rejected) of an AI proposal or of an existing AI-assisted revision, on behalf of the editor named in X-OV-Subject. AI-generated revisions cannot be published without an approving review by a person.",++ "summary": "Record the editorial queue's AI editor decision (approved or needs changes) on an existing revision, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject).",++ "description": "**wiki.review.ai_editor** (first-party): Record the editorial queue's AI editor decision (approved or needs changes) on an existing revision, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets AI-written text publish and index, and the page says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.\n\n**wiki.revision.publish** (first-party): Publish a revision now or at a scheduled time (idempotent), unpublish a page, and record a person's review (approved or rejected) of an AI proposal or of an existing AI-assisted revision, on behalf of the editor named in X-OV-Subject. AI-generated revisions cannot be published without an approving review by a person.","tags": [++ "wiki.review","wiki.revision"],"x-openvibe-capabilities": [++ "wiki.review.ai_editor","wiki.revision.publish"],"x-openvibe-visibility": [@@@ -849,6 +851,11 @@],"x-openvibe-quota-class": "wiki-write","security": [++ {++ "openvibeToken": [++ "wiki.review.ai_editor"++ ]++ },{"openvibeToken": ["wiki.revision.publish"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Work API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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"@@@ -591,10 +591,992 @@}}}++ },++ "/internal/config": {++ "get": {++ "operationId": "get_internal_config",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",++ "description": "**work.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "work.config"++ ],++ "x-openvibe-capabilities": [++ "work.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "work.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}": {++ "get": {++ "operationId": "get_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",++ "description": "**work.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "work.config"++ ],++ "x-openvibe-capabilities": [++ "work.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "work.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_internal_config_namespace",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",++ "description": "**work.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "work.config"++ ],++ "x-openvibe-capabilities": [++ "work.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "work.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/secrets/{key}": {++ "delete": {++ "operationId": "delete_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",++ "description": "**work.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "work.config"++ ],++ "x-openvibe-capabilities": [++ "work.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "work.config.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/common.config-change.v1"++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "values",++ "in": "query",++ "required": true,++ "schema": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."++ },++ {++ "name": "reason",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ {++ "name": "merge",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "description": "Merge into the active values instead of replacing them."++ },++ {++ "name": "unset",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "description": "Keys to remove from the new revision."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_internal_config_namespace_secrets_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",++ "description": "**work.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "work.config"++ ],++ "x-openvibe-capabilities": [++ "work.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "work.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/config/{namespace}/test/{key}": {++ "post": {++ "operationId": "post_internal_config_namespace_test_key",++ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",++ "description": "**work.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "tags": [++ "work.config"++ ],++ "x-openvibe-capabilities": [++ "work.config.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "config-manage",++ "security": [++ {++ "openvibeToken": [++ "work.config.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-change.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "key",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.config-namespace.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }}},"components": {"schemas": {++ "common.config-change.v1": {++ "title": "ConfigChange",++ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "values",++ "reason"++ ],++ "properties": {++ "values": {++ "type": "object",++ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value; a redaction marker keeps the value it stands for."++ }++ },++ "reason": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why the change is made; it is the revision's reason in the history."++ },++ "merge": {++ "type": "boolean",++ "default": false,++ "description": "Merge into the active values instead of replacing them."++ },++ "unset": {++ "type": "array",++ "maxItems": 256,++ "uniqueItems": true,++ "description": "Keys to remove from the new revision.",++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ }++ }++ },++ "common.config-namespace.v1": {++ "title": "ConfigNamespace",++ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "service",++ "namespace",++ "revision",++ "values",++ "classification",++ "sealed",++ "pins",++ "schema"++ ],++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."++ },++ "revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The active revision; null before the namespace has one."++ },++ "values": {++ "type": "object",++ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "sealed": {++ "type": "boolean",++ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."++ },++ "pins": {++ "type": "array",++ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ }++ },++ "schema": {++ "type": [++ "object",++ "null"++ ],++ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."++ },++ "active": {++ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "last_known_good": {++ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",++ "oneOf": [++ {++ "$ref": "#/components/schemas/common.config-snapshot.v1"++ },++ {++ "type": "null"++ }++ ]++ }++ },++ "$defs": {++ "marker": {++ "type": "object",++ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",++ "additionalProperties": false,++ "required": [++ "redacted",++ "fingerprint"++ ],++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."++ },++ "hint": {++ "type": "string",++ "minLength": 4,++ "maxLength": 4,++ "description": "The four-character hint (the value's last four characters), never more."++ },++ "set_at": {++ "type": "string",++ "format": "date-time"++ },++ "set_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "bound_to": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2048,++ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."++ }++ }++ }++ }++ },++ "common.config-snapshot.v1": {++ "title": "ConfigSnapshot",++ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",++ "type": "object",++ "required": [++ "service",++ "namespace",++ "revision",++ "previous_revision",++ "state",++ "values",++ "classification",++ "checksum",++ "created_at",++ "created_by"++ ],++ "additionalProperties": false,++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "namespace": {++ "type": "string",++ "maxLength": 120,++ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",++ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "previous_revision": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The revision that was active when this one was proposed; null for the namespace's first."++ },++ "state": {++ "enum": [++ "proposed",++ "active",++ "superseded",++ "rejected",++ "rolled_back"++ ]++ },++ "values": {++ "type": "object",++ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",++ "anyOf": [++ {++ "type": [++ "string",++ "number",++ "boolean",++ "null",++ "array"++ ]++ },++ {++ "type": "object",++ "additionalProperties": true,++ "not": {++ "required": [++ "redacted"++ ]++ }++ },++ {++ "type": "object",++ "required": [++ "redacted",++ "fingerprint"++ ],++ "additionalProperties": false,++ "properties": {++ "redacted": {++ "const": true++ },++ "fingerprint": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."++ }++ },++ "description": "A redacted secret-class value."++ }++ ]++ }++ },++ "classification": {++ "type": "object",++ "description": "Every key of values, classified. A key the service does not classify is internal.",++ "propertyNames": {++ "type": "string",++ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"++ },++ "additionalProperties": {++ "enum": [++ "public",++ "internal",++ "secret"++ ]++ }++ },++ "checksum": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$",++ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "created_by": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "activated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "When it became active; null while proposed, and for a rejected revision."++ },++ "activated_by": {++ "oneOf": [++ {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ {++ "type": "null"++ }++ ]++ },++ "reason": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500++ },++ "error": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 2000,++ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."++ },++ "copied_from": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "A rollback's source: the revision whose values this one copies."++ }++ },++ "allOf": [++ {++ "if": {++ "properties": {++ "state": {++ "const": "rejected"++ }++ }++ },++ "then": {++ "required": [++ "error"++ ],++ "properties": {++ "error": {++ "type": "string",++ "minLength": 1++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "state": {++ "enum": [++ "active",++ "superseded",++ "rolled_back"++ ]++ }++ }++ },++ "then": {++ "required": [++ "activated_at"++ ],++ "properties": {++ "activated_at": {++ "type": "string"++ }++ }++ }++ }++ ]++ },"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.",@@@ -661,6 +1643,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."++ }++ ]++ },"work.application-result.v1": {"title": "WorkApplicationResult","description": "work.application-result@1: what work.application.manage answers: one application, or a page of them (GET /api/v1/me/applications, newest first, ?status= and ?cursor=; `next` is the next page's cursor or null). DELETE answers 204 with no body.",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Zone API",-- "version": "0.314.0",-- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",++ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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"nnew file mode 100644@@@ -0,0 +1,23 @@++{++ "id": "blog.review.ai_editor",++ "version": "1.0",++ "owner": "blog",++ "status": "active",++ "visibility": "first-party",++ "description": "Record the editorial queue's AI editor decision (approved or rejected) on a post revision, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets an AI draft publish through the existing publish action and the post says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.",++ "permissions": [++ "blog:write"++ ],++ "resourceConstraints": [++ "none"++ ],++ "quotaClass": "blog-write",++ "events": [++ "blog.post.updated",++ "blog.index_document.upserted",++ "blog.index_document.deleted"++ ],++ "implementedBy": [++ "POST /api/v1/posts/:id/reviews"++ ]++}@@@ -2,9 +2,9 @@"id": "chat.config.manage","version": "1.0","owner": "chat",-- "status": "planned",++ "status": "active","visibility": "internal",-- "description": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe.Chat mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "description": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.","permissions": ["config:manage"],@@@ -2,9 +2,9 @@"id": "connect.config.manage","version": "1.0","owner": "connect",-- "status": "planned",++ "status": "active","visibility": "internal",-- "description": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe Connect mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "description": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.","permissions": ["config:manage"],@@@ -2,9 +2,9 @@"id": "deals.config.manage","version": "1.0","owner": "deals",-- "status": "planned",++ "status": "active","visibility": "internal",-- "description": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe.Deals mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "description": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.","permissions": ["config:manage"],@@@ -2,9 +2,9 @@"id": "design.config.manage","version": "1.0","owner": "design",-- "status": "planned",++ "status": "active","visibility": "internal",-- "description": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe.Design mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "description": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.","permissions": ["config:manage"],@@@ -2,9 +2,9 @@"id": "domains.config.manage","version": "1.0","owner": "domains",-- "status": "planned",++ "status": "active","visibility": "internal",-- "description": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe.Domains mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "description": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.","permissions": ["config:manage"],nnew file mode 100644@@@ -0,0 +1,24 @@++{++ "id": "news.review.ai_editor",++ "version": "1.0",++ "owner": "news",++ "status": "active",++ "visibility": "first-party",++ "description": "Record the editorial queue's AI editor decision (approved or rejected) on a story revision or an AI brief, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets AI text publish and index and the page says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.",++ "permissions": [++ "news:write"++ ],++ "resourceConstraints": [++ "none"++ ],++ "quotaClass": "news-write",++ "events": [++ "news.story.updated",++ "news.index_document.upserted",++ "news.index_document.deleted"++ ],++ "implementedBy": [++ "POST /api/v1/stories/:id/reviews",++ "POST /api/v1/briefs/:id/reviews"++ ]++}@@@ -2,9 +2,9 @@"id": "sources.config.manage","version": "1.0","owner": "sources",-- "status": "planned",++ "status": "active","visibility": "internal",-- "description": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe.Sources mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "description": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.","permissions": ["config:manage"],nnew file mode 100644@@@ -0,0 +1,21 @@++{++ "id": "wiki.review.ai_editor",++ "version": "1.0",++ "owner": "wiki",++ "status": "active",++ "visibility": "first-party",++ "description": "Record the editorial queue's AI editor decision (approved or needs changes) on an existing revision, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets AI-written text publish and index, and the page says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.",++ "permissions": [++ "wiki:review:ai_editor"++ ],++ "resourceConstraints": [],++ "quotaClass": "wiki-write",++ "events": [++ "wiki.page.updated",++ "wiki.index_document.upserted",++ "wiki.index_document.deleted"++ ],++ "implementedBy": [++ "POST /api/v1/pages/:id/revisions/:n/review"++ ]++}@@@ -2,9 +2,9 @@"id": "work.config.manage","version": "1.0","owner": "work",-- "status": "planned",++ "status": "active","visibility": "internal",-- "description": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe.Work mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",++ "description": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.","permissions": ["config:manage"],@@@ -53,6 +53,7 @@"blog.post.schedule","blog.post.unpublish","blog.post.delete",++ "blog.review.ai_editor","blog.feed.read"],"eventsProduced": [@@@ -16,6 +16,7 @@"chat.agent_poll.write","chat.analysis.read","chat.cache.invalidate",++ "chat.config.manage","chat.event.publish","chat.inbox.manage","chat.inbox.read",@@@ -15,7 +15,9 @@"state": "live","publicSite": "service"},-- "capabilities": [],++ "capabilities": [++ "connect.config.manage"++ ],"eventsProduced": ["connect.hook.received"],"eventsConsumed": ["network.account.export_requested", "network.account.deleted", "connect.hook.received"],"namespacesOwned": [@@@ -78,6 +78,7 @@"neighbourhood": "shop"},"capabilities": [++ "deals.config.manage","deals.offer.submit","deals.offer.update","deals.offer.expire",@@@ -15,7 +15,9 @@"state": "live","publicSite": "service"},-- "capabilities": [],++ "capabilities": [++ "design.config.manage"++ ],"eventsProduced": ["design.index_document.deleted","design.index_document.upserted"@@@ -17,6 +17,7 @@},"capabilities": ["domains.actions.read",++ "domains.config.manage","domains.proposal.decide"],"eventsProduced": [],@@@ -88,6 +88,7 @@"news.source.attach","news.timeline.update","news.perspective.update",++ "news.review.ai_editor","news.topic.manage"],"eventsProduced": [@@@ -20,6 +20,7 @@"note": "internal ingestion service; sources.openvibe.network says so (its API is loopback-only)"},"capabilities": [++ "sources.config.manage","sources.source.read","sources.item.read","sources.source.manage"@@@ -45,6 +45,7 @@"wiki.space.create","wiki.page.create","wiki.page.read",++ "wiki.review.ai_editor","wiki.revision.propose","wiki.revision.publish","wiki.revision.revert",@@@ -16,7 +16,8 @@"publicSite": "service"},"capabilities": [-- "work.application.manage"++ "work.application.manage",++ "work.config.manage"],"eventsProduced": ["work.index_document.upserted",@@@ -1,12 +1,12 @@{"name": "openvibe-contracts",-- "version": "0.311.0",++ "version": "0.315.0","lockfileVersion": 3,"requires": true,"packages": {"": {"name": "openvibe-contracts",-- "version": "0.311.0",++ "version": "0.315.0","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.314.0",++ "version": "0.315.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",@@@ -113,17 +113,16 @@ for (const cap of capabilities.manifests) {// openvibe-shared/config's admin routes. Only Network's admin proxy, acting for the owner, holds// them; they are never grantable to apps. Network uses its own admin config routes, not the proxy.// 0.311.0 (the owner's Setup tab): the config admin routes gained POST /internal/config/:namespace/test/:key--// (openvibe-shared 4.125.0's test hook), and eight more services that hold an owner input are reachable. Those--// eight are planned until each mounts the routes: then it turns active and its service manifest lists it (Deals'--// own test pins that list, so listing it early would break the service's pin bump).++// (openvibe-shared 4.125.0's test hook), and eight more services that hold an owner input are reachable. Seven of++// them now mount the routes and are active and listed in their service manifest; Media is still planned until it does.const CONFIG_ROUTES = ['GET /internal/config', 'GET|POST /internal/config/:namespace', 'PUT|DELETE /internal/config/:namespace/secrets/:key', 'POST /internal/config/:namespace/test/:key'];--for (const svc of ['ai', 'billing', 'services', 'watch', 'bench', 'report', 'host', 'codes', 'actor']) {++for (const svc of ['ai', 'billing', 'services', 'watch', 'bench', 'report', 'host', 'codes', 'actor', 'chat', 'connect', 'deals', 'work', 'sources', 'design', 'domains']) {const c = capabilities.get(`${svc}.config.manage`);ok(c && c.owner === svc && c.status === 'active' && c.visibility === 'internal', `${svc}.config.manage is an active internal capability`);ok(JSON.stringify(c.implementedBy) === JSON.stringify(CONFIG_ROUTES), `${svc}.config.manage names the config routes`);ok(/Only Network's admin proxy, acting for the owner, holds this capability/.test(c.description) && /never grantable to apps/.test(c.description), `${svc}.config.manage says who holds it and that an app never does`);}--for (const svc of ['chat', 'connect', 'deals', 'work', 'sources', 'design', 'domains', 'media']) {++for (const svc of ['media']) {const c = capabilities.get(`${svc}.config.manage`);ok(c && c.owner === svc && c.status === 'planned' && c.visibility === 'internal', `${svc}.config.manage is a planned internal capability`);ok(JSON.stringify(c.implementedBy) === JSON.stringify(CONFIG_ROUTES), `${svc}.config.manage names the config routes`);
@@ -1,5 +1,11 @@
@@ -1,5 +1,11 @@
# Changelog
# Changelog
+## 0.315.0 — 2026-11-07
+
+- The owner's Setup tab reaches seven more services: `<service>.config.manage` for `chat`, `connect`, `deals`, `work`, `sources`, `design` and `domains` is now **active** and listed in each service's manifest, because each mounts openvibe-shared/config's routes on `/internal/config` (`GET /internal/config`, `GET|POST /internal/config/:namespace`, `PUT|DELETE /internal/config/:namespace/secrets/:key`, `POST /internal/config/:namespace/test/:key`). Only Network's admin proxy, acting for the owner, holds them and they are never grantable to apps. `media.config.manage` stays planned until Media mounts the routes.
+- Add `blog.review.ai_editor`, `news.review.ai_editor` and `wiki.review.ai_editor`: the editorial queue's AI editor decision (approved or rejected; approved or needs changes on Wiki) recorded by the first-party service principal acting as itself (a `svc:` principal with no `X-OV-Subject`) through Blog's `POST /api/v1/posts/:id/reviews`, News's `POST /api/v1/stories/:id/reviews` and `POST /api/v1/briefs/:id/reviews`, and Wiki's `POST /api/v1/pages/:id/revisions/:n/review`. An approval lets AI-written text publish and index and the page says "Checked by OpenVibe's AI editor" with its date, never "Reviewed by a person"; each is granted only to the orchestrator's service principal. Their requests and results are a recorded schema gap (`compatibility/capability-schema-gaps.json`).
+- `generated/` refreshed: Connect and Design gain an OpenAPI document now that each has an active capability, and Blog, Chat, Deals, Domains, News, Sources, Wiki and Work gain the new operations.
+
## 0.314.0 — 2026-11-07
## 0.314.0 — 2026-11-07
- Add the capability `live.agent_sandbox.register` (Agent Streams S6): the chat plays sandbox worker's check-in on OpenVibe.Live, `POST /api/streams/agent/:id/plays/worker`, owned by Live and internal. Only a first-party service token whose principal holds it may register a worker, and the token names the person it works for in `X-OV-Subject`, who must own the stream. The owner's own session, a desk ticket (`wpt_`) and a person token are refused: an owner who could register a worker would declare their own unisolated machine a sandbox. The worker reports the isolation facts Live requires (no host network, no credentials, no profile, egress through the proxy, an ephemeral workspace) and Live derives health only from a check-in within the last minute; Live stores no endpoint and never calls the worker. Name the capability in Live's service manifest.
- Add the capability `live.agent_sandbox.register` (Agent Streams S6): the chat plays sandbox worker's check-in on OpenVibe.Live, `POST /api/streams/agent/:id/plays/worker`, owned by Live and internal. Only a first-party service token whose principal holds it may register a worker, and the token names the person it works for in `X-OV-Subject`, who must own the stream. The owner's own session, a desk ticket (`wpt_`) and a person token are refused: an owner who could register a worker would declare their own unisolated machine a sandbox. The worker reports the isolation facts Live requires (no host network, no credentials, no profile, egress through the proxy, an ephemeral workspace) and Live derives health only from a check-in within the last minute; Live stores no endpoint and never calls the worker. Name the capability in Live's service manifest.
@@ -28,6 +28,27 @@
@@ -28,6 +28,27 @@
"inputSchema",
"inputSchema",
"outputSchema"
"outputSchema"
]
]
+ },
+ {
+ "id": "blog.review.ai_editor",
+ "missing": [
+ "inputSchema",
+ "outputSchema"
+ ]
+ },
+ {
+ "id": "news.review.ai_editor",
+ "missing": [
+ "inputSchema",
+ "outputSchema"
+ ]
+ },
+ {
+ "id": "wiki.review.ai_editor",
+ "missing": [
+ "inputSchema",
+ "outputSchema"
+ ]
}
}
]
]
}
}
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Actor API",
"title": "OpenVibe.Actor API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.AI API",
"title": "OpenVibe.AI API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bench API",
"title": "OpenVibe.Bench API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Billing API",
"title": "OpenVibe.Billing API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Blog API",
"title": "OpenVibe.Blog API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -1075,6 +1075,55 @@
@@ -1075,6 +1075,55 @@
}
}
}
}
},
},
+ "/api/v1/posts/{id}/reviews": {
+ "post": {
+ "operationId": "post_api_v1_posts_id_reviews",
+ "summary": "Record the editorial queue's AI editor decision (approved or rejected) on a post revision, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject).",
+ "description": "**blog.review.ai_editor** (first-party): Record the editorial queue's AI editor decision (approved or rejected) on a post revision, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets an AI draft publish through the existing publish action and the post says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.",
+ "tags": [
+ "blog.review"
+ ],
+ "x-openvibe-capabilities": [
+ "blog.review.ai_editor"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "blog-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "blog.review.ai_editor"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/v1/posts/{id}/revisions": {
"/api/v1/posts/{id}/revisions": {
"get": {
"get": {
"operationId": "get_api_v1_posts_id_revisions",
"operationId": "get_api_v1_posts_id_revisions",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bot API",
"title": "OpenVibe.Bot API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Chat API",
"title": "OpenVibe.Chat API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -5110,45 +5110,96 @@
@@ -5110,45 +5110,96 @@
}
}
}
}
},
},
- "/internal/live/calls": {
- "post": {
- "operationId": "post_internal_live_calls",
- "summary": "Send a chat message to a room the actor is a member of (browsers over the chat socket; first-party services over Chat's service API).",
- "description": "**chat.message.send** (first-party): Send a chat message to a room the actor is a member of (browsers over the chat socket; first-party services over Chat's service API).",
+ "/internal/config": {
+ "get": {
+ "operationId": "get_internal_config",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",
+ "description": "**chat.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
"tags": [
"tags": [
- "chat.message"
+ "chat.config"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "chat.message.send"
+ "chat.config.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "first-party"
+ "internal"
],
],
- "x-openvibe-quota-class": "chat-send",
+ "x-openvibe-quota-class": "config-manage",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "chat.message.send"
+ "chat.config.manage"
]
]
}
}
],
],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/chat.send-request.v1"
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
}
}
- }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
}
}
- },
+ ],
"responses": {
"responses": {
"2XX": {
"2XX": {
"description": "Success",
"description": "Success",
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/chat.send-result.v1"
+ "$ref": "#/components/schemas/common.config-namespace.v1"
}
}
}
}
}
}
@@ -5166,36 +5217,95 @@
@@ -5166,36 +5217,95 @@
}
}
}
}
},
},
- "/internal/moderation/channels/{channelId}": {
+ "/internal/config/{namespace}": {
"get": {
"get": {
- "operationId": "get_internal_moderation_channels_channelId",
- "summary": "Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.C",
- "description": "**chat.moderation.read** (internal): Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.Chat owns (plan T3). Loopback only, service token.",
+ "operationId": "get_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",
+ "description": "**chat.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
"tags": [
"tags": [
- "chat.moderation"
+ "chat.config"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "chat.moderation.read"
+ "chat.config.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"internal"
"internal"
],
],
- "x-openvibe-quota-class": "internal-read",
+ "x-openvibe-quota-class": "config-manage",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "chat.moderation.read"
+ "chat.config.manage"
]
]
}
}
],
],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
"parameters": [
"parameters": [
{
{
- "name": "channelId",
+ "name": "namespace",
"in": "path",
"in": "path",
"required": true,
"required": true,
"schema": {
"schema": {
"type": "string"
"type": "string"
}
}
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
}
}
],
],
"responses": {
"responses": {
@@ -5204,7 +5314,7 @@
@@ -5204,7 +5314,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/chat.channel-moderation-result.v1"
+ "$ref": "#/components/schemas/common.config-namespace.v1"
}
}
}
}
}
}
@@ -5220,33 +5330,41 @@
@@ -5220,33 +5330,41 @@
}
}
}
}
}
}
- }
- },
- "/internal/moderation/channels/{channelId}/emote-count": {
- "get": {
- "operationId": "get_internal_moderation_channels_channelId_emote_count",
- "summary": "Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.C",
- "description": "**chat.moderation.read** (internal): Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.Chat owns (plan T3). Loopback only, service token.",
+ },
+ "post": {
+ "operationId": "post_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",
+ "description": "**chat.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
"tags": [
"tags": [
- "chat.moderation"
+ "chat.config"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "chat.moderation.read"
+ "chat.config.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"internal"
"internal"
],
],
- "x-openvibe-quota-class": "internal-read",
+ "x-openvibe-quota-class": "config-manage",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "chat.moderation.read"
+ "chat.config.manage"
]
]
}
}
],
],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
"parameters": [
"parameters": [
{
{
- "name": "channelId",
+ "name": "namespace",
"in": "path",
"in": "path",
"required": true,
"required": true,
"schema": {
"schema": {
@@ -5260,7 +5378,7 @@
@@ -5260,7 +5378,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/chat.channel-moderation-result.v1"
+ "$ref": "#/components/schemas/common.config-namespace.v1"
}
}
}
}
}
}
@@ -5278,36 +5396,103 @@
@@ -5278,36 +5396,103 @@
}
}
}
}
},
},
- "/internal/moderation/users/{userId}/channels": {
- "get": {
- "operationId": "get_internal_moderation_users_userId_channels",
- "summary": "Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.C",
- "description": "**chat.moderation.read** (internal): Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.Chat owns (plan T3). Loopback only, service token.",
+ "/internal/config/{namespace}/secrets/{key}": {
+ "delete": {
+ "operationId": "delete_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",
+ "description": "**chat.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
"tags": [
"tags": [
- "chat.moderation"
+ "chat.config"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "chat.moderation.read"
+ "chat.config.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"internal"
"internal"
],
],
- "x-openvibe-quota-class": "internal-read",
+ "x-openvibe-quota-class": "config-manage",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "chat.moderation.read"
+ "chat.config.manage"
]
]
}
}
],
],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
"parameters": [
"parameters": [
{
{
- "name": "userId",
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
"in": "path",
"in": "path",
"required": true,
"required": true,
"schema": {
"schema": {
"type": "string"
"type": "string"
}
}
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
}
}
],
],
"responses": {
"responses": {
@@ -5316,7 +5501,7 @@
@@ -5316,7 +5501,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/chat.channel-moderation-result.v1"
+ "$ref": "#/components/schemas/common.config-namespace.v1"
}
}
}
}
}
}
@@ -5332,50 +5517,420 @@
@@ -5332,50 +5517,420 @@
}
}
}
}
}
}
- }
- }
- },
- "components": {
- "schemas": {
- "chat.agent-digest.v1": {
- "title": "ChatAgentDigest",
- "description": "chat.agent-digest@1 (the Agent Streams plan, S5): what a chat room said to an agent between two of its steps, GET /api/v1/rooms/agent:<id>/digest?since= on OpenVibe.Chat. The agent never reads the raw room: it asks for a digest and gets at most ten items and 1,500 characters. Each item names who (the display name and the handle), what (cut to 200 characters), its kind (a question, a suggestion or a reaction) and its upvotes (a thumbs up on the message counts). Chat chooses what goes in: repeats are grouped, greetings and spam are dropped, and the most-upvoted questions and suggestions come first. Nobody is left out by accident: a person who is timed out or banned, whose account is younger than a day, or who set 'Agents don't read my messages' is never in the digest. A link is kept as its domain only ('a link to github.com'), because the agent never follows a link from chat. The code and the commands stay as text: the agent's own tools and approvals decide what may run, never chat.",
- "type": "object",
- "additionalProperties": false,
- "required": [
- "room",
- "since",
- "until",
- "items"
+ },
+ "put": {
+ "operationId": "put_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",
+ "description": "**chat.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "chat.config"
],
],
- "properties": {
- "room": {
- "type": "string",
- "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "The room the digest came from: an agent stream's room, `agent:<ags_id>`."
- },
- "since": {
- "type": [
- "string",
- "null"
- ],
- "format": "date-time",
- "description": "The start of the window: the moment the agent last read, or null for the first read."
- },
- "until": {
- "type": "string",
- "format": "date-time",
- "description": "The end of the window: the moment this digest was made."
+ "x-openvibe-capabilities": [
+ "chat.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "chat.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
},
},
- "items": {
- "type": "array",
- "maxItems": 10,
- "description": "What chat said, at most ten items. The order is the one Chat chose: the most-upvoted questions and suggestions first, the reactions last.",
- "items": {
- "type": "object",
- "additionalProperties": false,
- "required": [
- "who",
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/test/{key}": {
+ "post": {
+ "operationId": "post_internal_config_namespace_test_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",
+ "description": "**chat.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "chat.config"
+ ],
+ "x-openvibe-capabilities": [
+ "chat.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "chat.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/live/calls": {
+ "post": {
+ "operationId": "post_internal_live_calls",
+ "summary": "Send a chat message to a room the actor is a member of (browsers over the chat socket; first-party services over Chat's service API).",
+ "description": "**chat.message.send** (first-party): Send a chat message to a room the actor is a member of (browsers over the chat socket; first-party services over Chat's service API).",
+ "tags": [
+ "chat.message"
+ ],
+ "x-openvibe-capabilities": [
+ "chat.message.send"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "chat-send",
+ "security": [
+ {
+ "openvibeToken": [
+ "chat.message.send"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/chat.send-request.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/chat.send-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/moderation/channels/{channelId}": {
+ "get": {
+ "operationId": "get_internal_moderation_channels_channelId",
+ "summary": "Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.C",
+ "description": "**chat.moderation.read** (internal): Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.Chat owns (plan T3). Loopback only, service token.",
+ "tags": [
+ "chat.moderation"
+ ],
+ "x-openvibe-capabilities": [
+ "chat.moderation.read"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "internal-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "chat.moderation.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "channelId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/chat.channel-moderation-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/moderation/channels/{channelId}/emote-count": {
+ "get": {
+ "operationId": "get_internal_moderation_channels_channelId_emote_count",
+ "summary": "Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.C",
+ "description": "**chat.moderation.read** (internal): Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.Chat owns (plan T3). Loopback only, service token.",
+ "tags": [
+ "chat.moderation"
+ ],
+ "x-openvibe-capabilities": [
+ "chat.moderation.read"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "internal-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "chat.moderation.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "channelId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/chat.channel-moderation-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/moderation/users/{userId}/channels": {
+ "get": {
+ "operationId": "get_internal_moderation_users_userId_channels",
+ "summary": "Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.C",
+ "description": "**chat.moderation.read** (internal): Read one channel's moderation settings and moderator ids, the channels a person moderates, and a channel's custom emote count: how OpenVibe.Live reads what it needs from the six chat tables OpenVibe.Chat owns (plan T3). Loopback only, service token.",
+ "tags": [
+ "chat.moderation"
+ ],
+ "x-openvibe-capabilities": [
+ "chat.moderation.read"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "internal-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "chat.moderation.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "userId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/chat.channel-moderation-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "chat.agent-digest.v1": {
+ "title": "ChatAgentDigest",
+ "description": "chat.agent-digest@1 (the Agent Streams plan, S5): what a chat room said to an agent between two of its steps, GET /api/v1/rooms/agent:<id>/digest?since= on OpenVibe.Chat. The agent never reads the raw room: it asks for a digest and gets at most ten items and 1,500 characters. Each item names who (the display name and the handle), what (cut to 200 characters), its kind (a question, a suggestion or a reaction) and its upvotes (a thumbs up on the message counts). Chat chooses what goes in: repeats are grouped, greetings and spam are dropped, and the most-upvoted questions and suggestions come first. Nobody is left out by accident: a person who is timed out or banned, whose account is younger than a day, or who set 'Agents don't read my messages' is never in the digest. A link is kept as its domain only ('a link to github.com'), because the agent never follows a link from chat. The code and the commands stay as text: the agent's own tools and approvals decide what may run, never chat.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "room",
+ "since",
+ "until",
+ "items"
+ ],
+ "properties": {
+ "room": {
+ "type": "string",
+ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The room the digest came from: an agent stream's room, `agent:<ags_id>`."
+ },
+ "since": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "The start of the window: the moment the agent last read, or null for the first read."
+ },
+ "until": {
+ "type": "string",
+ "format": "date-time",
+ "description": "The end of the window: the moment this digest was made."
+ },
+ "items": {
+ "type": "array",
+ "maxItems": 10,
+ "description": "What chat said, at most ten items. The order is the one Chat chose: the most-upvoted questions and suggestions first, the reactions last.",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "who",
"what",
"what",
"kind",
"kind",
"upvotes"
"upvotes"
@@ -9817,164 +10372,591 @@
@@ -9817,164 +10372,591 @@
"maxLength": 2000,
"maxLength": 2000,
"description": "What they asked."
"description": "What they asked."
},
},
- "source": {
+ "source": {
+ "type": "object",
+ "required": [
+ "kind",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "kind": {
+ "enum": [
+ "voice"
+ ]
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_.:-]{1,64}$",
+ "description": "The source's stable id (the machine's device id): one chat per source."
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 80,
+ "description": "What to call it in the chat's name (the machine's name)."
+ }
+ }
+ },
+ "lang": {
+ "type": "string",
+ "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8}){0,2}$"
+ },
+ "idempotency_key": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_.:-]{1,128}$",
+ "description": "The caller's id for this ask (Bot: device id and the voice_ask id): asked again, the same answer, once."
+ }
+ }
+ },
+ "chat.vibe-ask-result.v1": {
+ "title": "ChatVibeAskResult",
+ "description": "chat.vibe-ask-result@1: Vibe's answer to a chat.vibe-ask-request@1. `answered` carries the reply (plain text, Markdown stripped, for speech); `refused` says why nothing was asked (Vibe resting, the person's daily allowance spent). `url` is the Vibe chat that keeps the question and the answer.",
+ "type": "object",
+ "required": [
+ "state",
+ "text"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "state": {
+ "enum": [
+ "answered",
+ "refused"
+ ]
+ },
+ "text": {
+ "type": "string",
+ "maxLength": 4000
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "pattern": "^https://"
+ },
+ "chat": {
+ "type": "string",
+ "pattern": "^v-[0-9a-f]{12}$",
+ "description": "The Vibe chat's slug."
+ },
+ "code": {
+ "type": "string",
+ "description": "Why it was refused (chat.vibe.off, chat.vibe.allowance)."
+ }
+ }
+ },
+ "chat.vibe-deliver-request.v1": {
+ "title": "ChatVibeDeliverRequest",
+ "description": "chat.vibe-deliver-request@1: a first-party service puts a result of the person's own work into their Vibe chats (OpenVibe.Actor: each finished run of one of their OpenVibe Agents, when the agent sends its results to Vibe). Chat keeps one chat per source, named after it, and posts the text there as Vibe's message, so each run is a chat they can open and continue. No AI answer is made and nothing counts against their allowance. POST /internal/chat/vibe/deliver, capability chat.vibe.deliver, loopback only.",
+ "type": "object",
+ "required": [
+ "subject",
+ "source",
+ "text",
+ "idempotency_key"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "subject": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The person whose work it is, as the calling service vouches (Actor: the agent's owner)."
+ },
+ "source": {
+ "type": "object",
+ "required": [
+ "kind",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "kind": {
+ "enum": [
+ "agent"
+ ]
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_.:-]{1,64}$",
+ "description": "The source's stable id (the agent's agt_ id): one chat per source."
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 80,
+ "description": "What to call it in the chat's name (the agent's name)."
+ }
+ }
+ },
+ "text": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 8000,
+ "description": "The result, in Markdown (as Vibe's answers are shown)."
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "pattern": "^https://",
+ "description": "Where the whole run is (its steps, sources and cost)."
+ },
+ "idempotency_key": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_.:-]{1,128}$",
+ "description": "The caller's id for this result (Actor: the run's task id): sent again, it is posted once."
+ }
+ }
+ },
+ "chat.vibe-deliver-result.v1": {
+ "title": "ChatVibeDeliverResult",
+ "description": "chat.vibe-deliver-result@1: where a chat.vibe-deliver-request@1 was posted. `repeated` is true when the idempotency key was seen before (nothing was posted again).",
+ "type": "object",
+ "required": [
+ "chat",
+ "url",
+ "repeated"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "chat": {
+ "type": "string",
+ "pattern": "^v-[0-9a-f]{12}$",
+ "description": "The Vibe chat's slug."
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "pattern": "^https://"
+ },
+ "repeated": {
+ "type": "boolean"
+ }
+ }
+ },
+ "common.config-change.v1": {
+ "title": "ConfigChange",
+ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "values",
+ "reason"
+ ],
+ "properties": {
+ "values": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "merge": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "unset": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ }
+ }
+ },
+ "common.config-namespace.v1": {
+ "title": "ConfigNamespace",
+ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "values",
+ "classification",
+ "sealed",
+ "pins",
+ "schema"
+ ],
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."
+ },
+ "revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The active revision; null before the namespace has one."
+ },
+ "values": {
+ "type": "object",
+ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "sealed": {
+ "type": "boolean",
+ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."
+ },
+ "pins": {
+ "type": "array",
+ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "schema": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."
+ },
+ "active": {
+ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "last_known_good": {
+ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "$defs": {
+ "marker": {
"type": "object",
"type": "object",
+ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",
+ "additionalProperties": false,
"required": [
"required": [
- "kind",
- "id"
+ "redacted",
+ "fingerprint"
],
],
- "additionalProperties": false,
"properties": {
"properties": {
- "kind": {
- "enum": [
- "voice"
- ]
+ "redacted": {
+ "const": true
},
},
- "id": {
+ "fingerprint": {
"type": "string",
"type": "string",
- "pattern": "^[A-Za-z0-9_.:-]{1,64}$",
- "description": "The source's stable id (the machine's device id): one chat per source."
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."
},
},
- "name": {
+ "hint": {
"type": "string",
"type": "string",
- "maxLength": 80,
- "description": "What to call it in the chat's name (the machine's name)."
+ "minLength": 4,
+ "maxLength": 4,
+ "description": "The four-character hint (the value's last four characters), never more."
+ },
+ "set_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "set_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "bound_to": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2048,
+ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."
}
}
}
}
- },
- "lang": {
- "type": "string",
- "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8}){0,2}$"
- },
- "idempotency_key": {
- "type": "string",
- "pattern": "^[A-Za-z0-9_.:-]{1,128}$",
- "description": "The caller's id for this ask (Bot: device id and the voice_ask id): asked again, the same answer, once."
}
}
}
}
},
},
- "chat.vibe-ask-result.v1": {
- "title": "ChatVibeAskResult",
- "description": "chat.vibe-ask-result@1: Vibe's answer to a chat.vibe-ask-request@1. `answered` carries the reply (plain text, Markdown stripped, for speech); `refused` says why nothing was asked (Vibe resting, the person's daily allowance spent). `url` is the Vibe chat that keeps the question and the answer.",
+ "common.config-snapshot.v1": {
+ "title": "ConfigSnapshot",
+ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",
"type": "object",
"type": "object",
"required": [
"required": [
+ "service",
+ "namespace",
+ "revision",
+ "previous_revision",
"state",
"state",
- "text"
+ "values",
+ "classification",
+ "checksum",
+ "created_at",
+ "created_by"
],
],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "previous_revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The revision that was active when this one was proposed; null for the namespace's first."
+ },
"state": {
"state": {
"enum": [
"enum": [
- "answered",
- "refused"
+ "proposed",
+ "active",
+ "superseded",
+ "rejected",
+ "rolled_back"
]
]
},
},
- "text": {
- "type": "string",
- "maxLength": 4000
+ "values": {
+ "type": "object",
+ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."
+ }
+ },
+ "description": "A redacted secret-class value."
+ }
+ ]
+ }
},
},
- "url": {
- "type": "string",
- "format": "uri",
- "pattern": "^https://"
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
},
},
- "chat": {
+ "checksum": {
"type": "string",
"type": "string",
- "pattern": "^v-[0-9a-f]{12}$",
- "description": "The Vibe chat's slug."
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."
},
},
- "code": {
- "type": "string",
- "description": "Why it was refused (chat.vibe.off, chat.vibe.allowance)."
- }
- }
- },
- "chat.vibe-deliver-request.v1": {
- "title": "ChatVibeDeliverRequest",
- "description": "chat.vibe-deliver-request@1: a first-party service puts a result of the person's own work into their Vibe chats (OpenVibe.Actor: each finished run of one of their OpenVibe Agents, when the agent sends its results to Vibe). Chat keeps one chat per source, named after it, and posts the text there as Vibe's message, so each run is a chat they can open and continue. No AI answer is made and nothing counts against their allowance. POST /internal/chat/vibe/deliver, capability chat.vibe.deliver, loopback only.",
- "type": "object",
- "required": [
- "subject",
- "source",
- "text",
- "idempotency_key"
- ],
- "additionalProperties": false,
- "properties": {
- "subject": {
+ "created_at": {
"type": "string",
"type": "string",
- "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "The person whose work it is, as the calling service vouches (Actor: the agent's owner)."
+ "format": "date-time"
},
},
- "source": {
- "type": "object",
- "required": [
- "kind",
- "id"
+ "created_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "activated_at": {
+ "type": [
+ "string",
+ "null"
],
],
- "additionalProperties": false,
- "properties": {
- "kind": {
- "enum": [
- "agent"
- ]
- },
- "id": {
- "type": "string",
- "pattern": "^[A-Za-z0-9_.:-]{1,64}$",
- "description": "The source's stable id (the agent's agt_ id): one chat per source."
+ "format": "date-time",
+ "description": "When it became active; null while proposed, and for a rejected revision."
+ },
+ "activated_by": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
},
},
- "name": {
- "type": "string",
- "maxLength": 80,
- "description": "What to call it in the chat's name (the agent's name)."
+ {
+ "type": "null"
}
}
- }
+ ]
},
},
- "text": {
- "type": "string",
- "minLength": 1,
- "maxLength": 8000,
- "description": "The result, in Markdown (as Vibe's answers are shown)."
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500
},
},
- "url": {
- "type": "string",
- "format": "uri",
- "pattern": "^https://",
- "description": "Where the whole run is (its steps, sources and cost)."
+ "error": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2000,
+ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."
},
},
- "idempotency_key": {
- "type": "string",
- "pattern": "^[A-Za-z0-9_.:-]{1,128}$",
- "description": "The caller's id for this result (Actor: the run's task id): sent again, it is posted once."
+ "copied_from": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "A rollback's source: the revision whose values this one copies."
}
}
- }
- },
- "chat.vibe-deliver-result.v1": {
- "title": "ChatVibeDeliverResult",
- "description": "chat.vibe-deliver-result@1: where a chat.vibe-deliver-request@1 was posted. `repeated` is true when the idempotency key was seen before (nothing was posted again).",
- "type": "object",
- "required": [
- "chat",
- "url",
- "repeated"
- ],
- "additionalProperties": false,
- "properties": {
- "chat": {
- "type": "string",
- "pattern": "^v-[0-9a-f]{12}$",
- "description": "The Vibe chat's slug."
- },
- "url": {
- "type": "string",
- "format": "uri",
- "pattern": "^https://"
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "const": "rejected"
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "error"
+ ],
+ "properties": {
+ "error": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
},
},
- "repeated": {
- "type": "boolean"
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "enum": [
+ "active",
+ "superseded",
+ "rolled_back"
+ ]
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "activated_at"
+ ],
+ "properties": {
+ "activated_at": {
+ "type": "string"
+ }
+ }
+ }
}
}
- }
+ ]
},
},
"common.owned-resource-control-request.v1": {
"common.owned-resource-control-request.v1": {
"title": "OwnedResourceControlRequest",
"title": "OwnedResourceControlRequest",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Codes API",
"title": "OpenVibe.Codes API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Community API",
"title": "OpenVibe.Community API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
new file mode 100644
new file mode 100644
@@ -0,0 +1,1197 @@
@@ -0,0 +1,1197 @@
+{
+ "openapi": "3.1.0",
+ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
+ "info": {
+ "title": "OpenVibe Connect API",
+ "version": "0.315.0",
+ "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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://connect.openvibe.website"
+ }
+ ],
+ "paths": {
+ "/internal/config": {
+ "get": {
+ "operationId": "get_internal_config",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**connect.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "connect.config"
+ ],
+ "x-openvibe-capabilities": [
+ "connect.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "connect.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}": {
+ "get": {
+ "operationId": "get_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**connect.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "connect.config"
+ ],
+ "x-openvibe-capabilities": [
+ "connect.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "connect.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**connect.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "connect.config"
+ ],
+ "x-openvibe-capabilities": [
+ "connect.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "connect.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/secrets/{key}": {
+ "delete": {
+ "operationId": "delete_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**connect.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "connect.config"
+ ],
+ "x-openvibe-capabilities": [
+ "connect.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "connect.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**connect.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "connect.config"
+ ],
+ "x-openvibe-capabilities": [
+ "connect.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "connect.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/test/{key}": {
+ "post": {
+ "operationId": "post_internal_config_namespace_test_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**connect.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "connect.config"
+ ],
+ "x-openvibe-capabilities": [
+ "connect.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "connect.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "common.config-change.v1": {
+ "title": "ConfigChange",
+ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "values",
+ "reason"
+ ],
+ "properties": {
+ "values": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "merge": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "unset": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ }
+ }
+ },
+ "common.config-namespace.v1": {
+ "title": "ConfigNamespace",
+ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "values",
+ "classification",
+ "sealed",
+ "pins",
+ "schema"
+ ],
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."
+ },
+ "revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The active revision; null before the namespace has one."
+ },
+ "values": {
+ "type": "object",
+ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "sealed": {
+ "type": "boolean",
+ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."
+ },
+ "pins": {
+ "type": "array",
+ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "schema": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."
+ },
+ "active": {
+ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "last_known_good": {
+ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "$defs": {
+ "marker": {
+ "type": "object",
+ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",
+ "additionalProperties": false,
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."
+ },
+ "hint": {
+ "type": "string",
+ "minLength": 4,
+ "maxLength": 4,
+ "description": "The four-character hint (the value's last four characters), never more."
+ },
+ "set_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "set_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "bound_to": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2048,
+ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."
+ }
+ }
+ }
+ }
+ },
+ "common.config-snapshot.v1": {
+ "title": "ConfigSnapshot",
+ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",
+ "type": "object",
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "previous_revision",
+ "state",
+ "values",
+ "classification",
+ "checksum",
+ "created_at",
+ "created_by"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "previous_revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The revision that was active when this one was proposed; null for the namespace's first."
+ },
+ "state": {
+ "enum": [
+ "proposed",
+ "active",
+ "superseded",
+ "rejected",
+ "rolled_back"
+ ]
+ },
+ "values": {
+ "type": "object",
+ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."
+ }
+ },
+ "description": "A redacted secret-class value."
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "checksum": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "created_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "activated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "When it became active; null while proposed, and for a rejected revision."
+ },
+ "activated_by": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500
+ },
+ "error": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2000,
+ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."
+ },
+ "copied_from": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "A rollback's source: the revision whose values this one copies."
+ }
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "const": "rejected"
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "error"
+ ],
+ "properties": {
+ "error": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "enum": [
+ "active",
+ "superseded",
+ "rolled_back"
+ ]
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "activated_at"
+ ],
+ "properties": {
+ "activated_at": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ ]
+ },
+ "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}."
+ }
+ }
+ },
+ "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": {
+ "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."
+ }
+ }
+ }
+}
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Coupons API",
"title": "OpenVibe.Coupons API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Deals API",
"title": "OpenVibe.Deals API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -1112,10 +1112,992 @@
@@ -1112,10 +1112,992 @@
}
}
}
}
}
}
+ },
+ "/internal/config": {
+ "get": {
+ "operationId": "get_internal_config",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists t",
+ "description": "**deals.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "deals.config"
+ ],
+ "x-openvibe-capabilities": [
+ "deals.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "deals.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}": {
+ "get": {
+ "operationId": "get_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists t",
+ "description": "**deals.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "deals.config"
+ ],
+ "x-openvibe-capabilities": [
+ "deals.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "deals.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists t",
+ "description": "**deals.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "deals.config"
+ ],
+ "x-openvibe-capabilities": [
+ "deals.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "deals.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/secrets/{key}": {
+ "delete": {
+ "operationId": "delete_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists t",
+ "description": "**deals.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "deals.config"
+ ],
+ "x-openvibe-capabilities": [
+ "deals.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "deals.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists t",
+ "description": "**deals.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "deals.config"
+ ],
+ "x-openvibe-capabilities": [
+ "deals.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "deals.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/test/{key}": {
+ "post": {
+ "operationId": "post_internal_config_namespace_test_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists t",
+ "description": "**deals.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "deals.config"
+ ],
+ "x-openvibe-capabilities": [
+ "deals.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "deals.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
}
}
},
},
"components": {
"components": {
"schemas": {
"schemas": {
+ "common.config-change.v1": {
+ "title": "ConfigChange",
+ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "values",
+ "reason"
+ ],
+ "properties": {
+ "values": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "merge": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "unset": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ }
+ }
+ },
+ "common.config-namespace.v1": {
+ "title": "ConfigNamespace",
+ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "values",
+ "classification",
+ "sealed",
+ "pins",
+ "schema"
+ ],
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."
+ },
+ "revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The active revision; null before the namespace has one."
+ },
+ "values": {
+ "type": "object",
+ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "sealed": {
+ "type": "boolean",
+ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."
+ },
+ "pins": {
+ "type": "array",
+ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "schema": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."
+ },
+ "active": {
+ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "last_known_good": {
+ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "$defs": {
+ "marker": {
+ "type": "object",
+ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",
+ "additionalProperties": false,
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."
+ },
+ "hint": {
+ "type": "string",
+ "minLength": 4,
+ "maxLength": 4,
+ "description": "The four-character hint (the value's last four characters), never more."
+ },
+ "set_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "set_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "bound_to": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2048,
+ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."
+ }
+ }
+ }
+ }
+ },
+ "common.config-snapshot.v1": {
+ "title": "ConfigSnapshot",
+ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",
+ "type": "object",
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "previous_revision",
+ "state",
+ "values",
+ "classification",
+ "checksum",
+ "created_at",
+ "created_by"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "previous_revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The revision that was active when this one was proposed; null for the namespace's first."
+ },
+ "state": {
+ "enum": [
+ "proposed",
+ "active",
+ "superseded",
+ "rejected",
+ "rolled_back"
+ ]
+ },
+ "values": {
+ "type": "object",
+ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."
+ }
+ },
+ "description": "A redacted secret-class value."
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "checksum": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "created_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "activated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "When it became active; null while proposed, and for a rejected revision."
+ },
+ "activated_by": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500
+ },
+ "error": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2000,
+ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."
+ },
+ "copied_from": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "A rollback's source: the revision whose values this one copies."
+ }
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "const": "rejected"
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "error"
+ ],
+ "properties": {
+ "error": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "enum": [
+ "active",
+ "superseded",
+ "rolled_back"
+ ]
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "activated_at"
+ ],
+ "properties": {
+ "activated_at": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ ]
+ },
"common.no-body.v1": {
"common.no-body.v1": {
"title": "NoBody",
"title": "NoBody",
"description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",
"description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",
@@ -3001,6 +3983,123 @@
@@ -3001,6 +3983,123 @@
"description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."
"description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."
}
}
}
}
+ },
+ "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": {
"securitySchemes": {
new file mode 100644
new file mode 100644
@@ -0,0 +1,1197 @@
@@ -0,0 +1,1197 @@
+{
+ "openapi": "3.1.0",
+ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
+ "info": {
+ "title": "OpenVibe.Design API",
+ "version": "0.315.0",
+ "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for 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.design"
+ }
+ ],
+ "paths": {
+ "/internal/config": {
+ "get": {
+ "operationId": "get_internal_config",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists ",
+ "description": "**design.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "design.config"
+ ],
+ "x-openvibe-capabilities": [
+ "design.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "design.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}": {
+ "get": {
+ "operationId": "get_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists ",
+ "description": "**design.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "design.config"
+ ],
+ "x-openvibe-capabilities": [
+ "design.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "design.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists ",
+ "description": "**design.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "design.config"
+ ],
+ "x-openvibe-capabilities": [
+ "design.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "design.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/secrets/{key}": {
+ "delete": {
+ "operationId": "delete_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists ",
+ "description": "**design.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "design.config"
+ ],
+ "x-openvibe-capabilities": [
+ "design.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "design.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists ",
+ "description": "**design.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "design.config"
+ ],
+ "x-openvibe-capabilities": [
+ "design.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "design.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/test/{key}": {
+ "post": {
+ "operationId": "post_internal_config_namespace_test_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists ",
+ "description": "**design.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "design.config"
+ ],
+ "x-openvibe-capabilities": [
+ "design.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "design.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "common.config-change.v1": {
+ "title": "ConfigChange",
+ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "values",
+ "reason"
+ ],
+ "properties": {
+ "values": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "merge": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "unset": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ }
+ }
+ },
+ "common.config-namespace.v1": {
+ "title": "ConfigNamespace",
+ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "values",
+ "classification",
+ "sealed",
+ "pins",
+ "schema"
+ ],
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."
+ },
+ "revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The active revision; null before the namespace has one."
+ },
+ "values": {
+ "type": "object",
+ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "sealed": {
+ "type": "boolean",
+ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."
+ },
+ "pins": {
+ "type": "array",
+ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "schema": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."
+ },
+ "active": {
+ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "last_known_good": {
+ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "$defs": {
+ "marker": {
+ "type": "object",
+ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",
+ "additionalProperties": false,
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."
+ },
+ "hint": {
+ "type": "string",
+ "minLength": 4,
+ "maxLength": 4,
+ "description": "The four-character hint (the value's last four characters), never more."
+ },
+ "set_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "set_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "bound_to": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2048,
+ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."
+ }
+ }
+ }
+ }
+ },
+ "common.config-snapshot.v1": {
+ "title": "ConfigSnapshot",
+ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",
+ "type": "object",
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "previous_revision",
+ "state",
+ "values",
+ "classification",
+ "checksum",
+ "created_at",
+ "created_by"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "previous_revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The revision that was active when this one was proposed; null for the namespace's first."
+ },
+ "state": {
+ "enum": [
+ "proposed",
+ "active",
+ "superseded",
+ "rejected",
+ "rolled_back"
+ ]
+ },
+ "values": {
+ "type": "object",
+ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."
+ }
+ },
+ "description": "A redacted secret-class value."
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "checksum": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "created_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "activated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "When it became active; null while proposed, and for a rejected revision."
+ },
+ "activated_by": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500
+ },
+ "error": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2000,
+ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."
+ },
+ "copied_from": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "A rollback's source: the revision whose values this one copies."
+ }
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "const": "rejected"
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "error"
+ ],
+ "properties": {
+ "error": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "enum": [
+ "active",
+ "superseded",
+ "rolled_back"
+ ]
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "activated_at"
+ ],
+ "properties": {
+ "activated_at": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ ]
+ },
+ "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}."
+ }
+ }
+ },
+ "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": {
+ "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."
+ }
+ }
+ }
+}
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Domains API",
"title": "OpenVibe.Domains API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -117,6 +117,561 @@
@@ -117,6 +117,561 @@
}
}
}
}
}
}
+ },
+ "/internal/config": {
+ "get": {
+ "operationId": "get_internal_config",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**domains.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "domains.config"
+ ],
+ "x-openvibe-capabilities": [
+ "domains.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "domains.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}": {
+ "get": {
+ "operationId": "get_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**domains.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "domains.config"
+ ],
+ "x-openvibe-capabilities": [
+ "domains.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "domains.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**domains.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "domains.config"
+ ],
+ "x-openvibe-capabilities": [
+ "domains.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "domains.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/secrets/{key}": {
+ "delete": {
+ "operationId": "delete_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**domains.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "domains.config"
+ ],
+ "x-openvibe-capabilities": [
+ "domains.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "domains.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**domains.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "domains.config"
+ ],
+ "x-openvibe-capabilities": [
+ "domains.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "domains.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/test/{key}": {
+ "post": {
+ "operationId": "post_internal_config_namespace_test_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**domains.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "domains.config"
+ ],
+ "x-openvibe-capabilities": [
+ "domains.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "domains.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
}
}
},
},
"components": {
"components": {
@@ -285,6 +840,433 @@
@@ -285,6 +840,433 @@
}
}
}
}
},
},
+ "common.config-change.v1": {
+ "title": "ConfigChange",
+ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "values",
+ "reason"
+ ],
+ "properties": {
+ "values": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "merge": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "unset": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ }
+ }
+ },
+ "common.config-namespace.v1": {
+ "title": "ConfigNamespace",
+ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "values",
+ "classification",
+ "sealed",
+ "pins",
+ "schema"
+ ],
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."
+ },
+ "revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The active revision; null before the namespace has one."
+ },
+ "values": {
+ "type": "object",
+ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "sealed": {
+ "type": "boolean",
+ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."
+ },
+ "pins": {
+ "type": "array",
+ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "schema": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."
+ },
+ "active": {
+ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "last_known_good": {
+ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "$defs": {
+ "marker": {
+ "type": "object",
+ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",
+ "additionalProperties": false,
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."
+ },
+ "hint": {
+ "type": "string",
+ "minLength": 4,
+ "maxLength": 4,
+ "description": "The four-character hint (the value's last four characters), never more."
+ },
+ "set_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "set_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "bound_to": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2048,
+ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."
+ }
+ }
+ }
+ }
+ },
+ "common.config-snapshot.v1": {
+ "title": "ConfigSnapshot",
+ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",
+ "type": "object",
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "previous_revision",
+ "state",
+ "values",
+ "classification",
+ "checksum",
+ "created_at",
+ "created_by"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "previous_revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The revision that was active when this one was proposed; null for the namespace's first."
+ },
+ "state": {
+ "enum": [
+ "proposed",
+ "active",
+ "superseded",
+ "rejected",
+ "rolled_back"
+ ]
+ },
+ "values": {
+ "type": "object",
+ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."
+ }
+ },
+ "description": "A redacted secret-class value."
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "checksum": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "created_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "activated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "When it became active; null while proposed, and for a rejected revision."
+ },
+ "activated_by": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500
+ },
+ "error": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2000,
+ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."
+ },
+ "copied_from": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "A rollback's source: the revision whose values this one copies."
+ }
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "const": "rejected"
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "error"
+ ],
+ "properties": {
+ "error": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "enum": [
+ "active",
+ "superseded",
+ "rolled_back"
+ ]
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "activated_at"
+ ],
+ "properties": {
+ "activated_at": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ ]
+ },
"errors.problem.v1": {
"errors.problem.v1": {
"title": "Problem",
"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.",
"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.",
@@ -350,6 +1332,123 @@
@@ -350,6 +1332,123 @@
"description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."
"description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."
}
}
}
}
+ },
+ "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": {
"securitySchemes": {
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Events API",
"title": "OpenVibe.Events API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Food API",
"title": "OpenVibe.Food API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Games API",
"title": "OpenVibe.Games API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Host API",
"title": "OpenVibe.Host API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -33,8 +33,8 @@
@@ -33,8 +33,8 @@
"service": "blog",
"service": "blog",
"name": "OpenVibe.Blog",
"name": "OpenVibe.Blog",
"origin": "https://openvibe.blog",
"origin": "https://openvibe.blog",
- "operations": 22,
- "capabilities": 12
+ "operations": 23,
+ "capabilities": 13
},
},
{
{
"service": "bot",
"service": "bot",
@@ -47,8 +47,8 @@
@@ -47,8 +47,8 @@
"service": "chat",
"service": "chat",
"name": "OpenVibe.Chat",
"name": "OpenVibe.Chat",
"origin": "https://openvibe.chat",
"origin": "https://openvibe.chat",
- "operations": 60,
- "capabilities": 24
+ "operations": 66,
+ "capabilities": 25
},
},
{
{
"service": "codes",
"service": "codes",
@@ -64,6 +64,13 @@
@@ -64,6 +64,13 @@
"operations": 40,
"operations": 40,
"capabilities": 13
"capabilities": 13
},
},
+ {
+ "service": "connect",
+ "name": "OpenVibe Connect",
+ "origin": "https://connect.openvibe.website",
+ "operations": 6,
+ "capabilities": 1
+ },
{
{
"service": "coupons",
"service": "coupons",
"name": "OpenVibe.Coupons",
"name": "OpenVibe.Coupons",
@@ -75,15 +82,22 @@
@@ -75,15 +82,22 @@
"service": "deals",
"service": "deals",
"name": "OpenVibe.Deals",
"name": "OpenVibe.Deals",
"origin": "https://openvibe.deals",
"origin": "https://openvibe.deals",
- "operations": 18,
- "capabilities": 12
+ "operations": 24,
+ "capabilities": 13
+ },
+ {
+ "service": "design",
+ "name": "OpenVibe.Design",
+ "origin": "https://openvibe.design",
+ "operations": 6,
+ "capabilities": 1
},
},
{
{
"service": "domains",
"service": "domains",
"name": "OpenVibe.Domains",
"name": "OpenVibe.Domains",
"origin": "https://openvibe.domains",
"origin": "https://openvibe.domains",
- "operations": 2,
- "capabilities": 2
+ "operations": 8,
+ "capabilities": 3
},
},
{
{
"service": "events",
"service": "events",
@@ -152,8 +166,8 @@
@@ -152,8 +166,8 @@
"service": "news",
"service": "news",
"name": "OpenVibe.News",
"name": "OpenVibe.News",
"origin": "https://openvibe.news",
"origin": "https://openvibe.news",
- "operations": 26,
- "capabilities": 11
+ "operations": 28,
+ "capabilities": 12
},
},
{
{
"service": "openre",
"service": "openre",
@@ -215,8 +229,8 @@
@@ -215,8 +229,8 @@
"service": "sources",
"service": "sources",
"name": "OpenVibe.Sources",
"name": "OpenVibe.Sources",
"origin": "https://sources.openvibe.network",
"origin": "https://sources.openvibe.network",
- "operations": 13,
- "capabilities": 3
+ "operations": 19,
+ "capabilities": 4
},
},
{
{
"service": "space",
"service": "space",
@@ -279,14 +293,14 @@
@@ -279,14 +293,14 @@
"name": "OpenVibe.Wiki",
"name": "OpenVibe.Wiki",
"origin": "https://openvibe.wiki",
"origin": "https://openvibe.wiki",
"operations": 25,
"operations": 25,
- "capabilities": 8
+ "capabilities": 9
},
},
{
{
"service": "work",
"service": "work",
"name": "OpenVibe.Work",
"name": "OpenVibe.Work",
"origin": "https://openvibe.work",
"origin": "https://openvibe.work",
- "operations": 5,
- "capabilities": 1
+ "operations": 11,
+ "capabilities": 2
},
},
{
{
"service": "zone",
"service": "zone",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Inventory API",
"title": "OpenVibe.Inventory API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Live API",
"title": "OpenVibe.Live API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.MediaHub API",
"title": "OpenVibe.MediaHub API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Media API",
"title": "OpenVibe.Media API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Network API",
"title": "OpenVibe.Network API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.News API",
"title": "OpenVibe.News API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -16,6 +16,55 @@
@@ -16,6 +16,55 @@
}
}
],
],
"paths": {
"paths": {
+ "/api/v1/briefs/{id}/reviews": {
+ "post": {
+ "operationId": "post_api_v1_briefs_id_reviews",
+ "summary": "Record the editorial queue's AI editor decision (approved or rejected) on a story revision or an AI brief, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject).",
+ "description": "**news.review.ai_editor** (first-party): Record the editorial queue's AI editor decision (approved or rejected) on a story revision or an AI brief, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets AI text publish and index and the page says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.",
+ "tags": [
+ "news.review"
+ ],
+ "x-openvibe-capabilities": [
+ "news.review.ai_editor"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "news-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "news.review.ai_editor"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/v1/clusters": {
"/api/v1/clusters": {
"get": {
"get": {
"operationId": "get_api_v1_clusters",
"operationId": "get_api_v1_clusters",
@@ -983,6 +1032,55 @@
@@ -983,6 +1032,55 @@
}
}
}
}
},
},
+ "/api/v1/stories/{id}/reviews": {
+ "post": {
+ "operationId": "post_api_v1_stories_id_reviews",
+ "summary": "Record the editorial queue's AI editor decision (approved or rejected) on a story revision or an AI brief, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject).",
+ "description": "**news.review.ai_editor** (first-party): Record the editorial queue's AI editor decision (approved or rejected) on a story revision or an AI brief, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets AI text publish and index and the page says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.",
+ "tags": [
+ "news.review"
+ ],
+ "x-openvibe-capabilities": [
+ "news.review.ai_editor"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "news-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "news.review.ai_editor"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/v1/stories/{id}/revisions": {
"/api/v1/stories/{id}/revisions": {
"get": {
"get": {
"operationId": "get_api_v1_stories_id_revisions",
"operationId": "get_api_v1_stories_id_revisions",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenRestream API",
"title": "OpenRestream API",
- "version": "0.314.0",
- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Play API",
"title": "OpenVibe.Play API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Quest API",
"title": "OpenVibe.Quest API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Report API",
"title": "OpenVibe.Report API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Reviews API",
"title": "OpenVibe.Reviews API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Run API",
"title": "OpenVibe.Run API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Search API",
"title": "OpenVibe.Search API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Services API",
"title": "OpenVibe.Services API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Sources API",
"title": "OpenVibe.Sources API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -731,10 +731,992 @@
@@ -731,10 +731,992 @@
}
}
}
}
}
}
+ },
+ "/internal/config": {
+ "get": {
+ "operationId": "get_internal_config",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**sources.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "sources.config"
+ ],
+ "x-openvibe-capabilities": [
+ "sources.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "sources.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}": {
+ "get": {
+ "operationId": "get_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**sources.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "sources.config"
+ ],
+ "x-openvibe-capabilities": [
+ "sources.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "sources.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**sources.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "sources.config"
+ ],
+ "x-openvibe-capabilities": [
+ "sources.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "sources.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/secrets/{key}": {
+ "delete": {
+ "operationId": "delete_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**sources.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "sources.config"
+ ],
+ "x-openvibe-capabilities": [
+ "sources.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "sources.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**sources.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "sources.config"
+ ],
+ "x-openvibe-capabilities": [
+ "sources.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "sources.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/test/{key}": {
+ "post": {
+ "operationId": "post_internal_config_namespace_test_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists",
+ "description": "**sources.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "sources.config"
+ ],
+ "x-openvibe-capabilities": [
+ "sources.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "sources.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
}
}
},
},
"components": {
"components": {
"schemas": {
"schemas": {
+ "common.config-change.v1": {
+ "title": "ConfigChange",
+ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "values",
+ "reason"
+ ],
+ "properties": {
+ "values": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "merge": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "unset": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ }
+ }
+ },
+ "common.config-namespace.v1": {
+ "title": "ConfigNamespace",
+ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "values",
+ "classification",
+ "sealed",
+ "pins",
+ "schema"
+ ],
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."
+ },
+ "revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The active revision; null before the namespace has one."
+ },
+ "values": {
+ "type": "object",
+ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "sealed": {
+ "type": "boolean",
+ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."
+ },
+ "pins": {
+ "type": "array",
+ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "schema": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."
+ },
+ "active": {
+ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "last_known_good": {
+ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "$defs": {
+ "marker": {
+ "type": "object",
+ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",
+ "additionalProperties": false,
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."
+ },
+ "hint": {
+ "type": "string",
+ "minLength": 4,
+ "maxLength": 4,
+ "description": "The four-character hint (the value's last four characters), never more."
+ },
+ "set_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "set_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "bound_to": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2048,
+ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."
+ }
+ }
+ }
+ }
+ },
+ "common.config-snapshot.v1": {
+ "title": "ConfigSnapshot",
+ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",
+ "type": "object",
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "previous_revision",
+ "state",
+ "values",
+ "classification",
+ "checksum",
+ "created_at",
+ "created_by"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "previous_revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The revision that was active when this one was proposed; null for the namespace's first."
+ },
+ "state": {
+ "enum": [
+ "proposed",
+ "active",
+ "superseded",
+ "rejected",
+ "rolled_back"
+ ]
+ },
+ "values": {
+ "type": "object",
+ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."
+ }
+ },
+ "description": "A redacted secret-class value."
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "checksum": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "created_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "activated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "When it became active; null while proposed, and for a rejected revision."
+ },
+ "activated_by": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500
+ },
+ "error": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2000,
+ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."
+ },
+ "copied_from": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "A rollback's source: the revision whose values this one copies."
+ }
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "const": "rejected"
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "error"
+ ],
+ "properties": {
+ "error": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "enum": [
+ "active",
+ "superseded",
+ "rolled_back"
+ ]
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "activated_at"
+ ],
+ "properties": {
+ "activated_at": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ ]
+ },
"common.no-body.v1": {
"common.no-body.v1": {
"title": "NoBody",
"title": "NoBody",
"description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",
"description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",
@@ -807,6 +1789,123 @@
@@ -807,6 +1789,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."
+ }
+ ]
+ },
"sources.item-read-result.v1": {
"sources.item-read-result.v1": {
"title": "SourcesItemReadResult",
"title": "SourcesItemReadResult",
"description": "sources.item-read-result@1: answers of sources.item.read on OpenVibe.Sources. GET /api/v1/items?source=&category=&after=&limit=&include_removed= → { items, next_after, more, sources } (a change feed: items in change order, at most 500; sources gives each listed source's health); GET /api/v1/items/:id[?revisions=1] → { item, source } (the item, with its revisions when asked, and its source's health; null when the source is gone).",
"description": "sources.item-read-result@1: answers of sources.item.read on OpenVibe.Sources. GET /api/v1/items?source=&category=&after=&limit=&include_removed= → { items, next_after, more, sources } (a change feed: items in change order, at most 500; sources gives each listed source's health); GET /api/v1/items/:id[?revisions=1] → { item, source } (the item, with its revisions when asked, and its source's health; null when the source is gone).",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Space API",
"title": "OpenVibe.Space API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tips API",
"title": "OpenVibe.Tips API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Today API",
"title": "OpenVibe.Today API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tools API",
"title": "OpenVibe.Tools API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Trade API",
"title": "OpenVibe.Trade API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.VIP API",
"title": "OpenVibe.VIP API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Vision API",
"title": "OpenVibe.Vision API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Watch API",
"title": "OpenVibe.Watch API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Wiki API",
"title": "OpenVibe.Wiki API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -836,12 +836,14 @@
@@ -836,12 +836,14 @@
"/api/v1/pages/{id}/revisions/{n}/review": {
"/api/v1/pages/{id}/revisions/{n}/review": {
"post": {
"post": {
"operationId": "post_api_v1_pages_id_revisions_n_review",
"operationId": "post_api_v1_pages_id_revisions_n_review",
- "summary": "Publish a revision now or at a scheduled time (idempotent), unpublish a page, and record a person's review (approved or rejected) of an AI proposal or of an existing AI-assisted revision, on behalf of",
- "description": "**wiki.revision.publish** (first-party): Publish a revision now or at a scheduled time (idempotent), unpublish a page, and record a person's review (approved or rejected) of an AI proposal or of an existing AI-assisted revision, on behalf of the editor named in X-OV-Subject. AI-generated revisions cannot be published without an approving review by a person.",
+ "summary": "Record the editorial queue's AI editor decision (approved or needs changes) on an existing revision, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject).",
+ "description": "**wiki.review.ai_editor** (first-party): Record the editorial queue's AI editor decision (approved or needs changes) on an existing revision, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets AI-written text publish and index, and the page says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.\n\n**wiki.revision.publish** (first-party): Publish a revision now or at a scheduled time (idempotent), unpublish a page, and record a person's review (approved or rejected) of an AI proposal or of an existing AI-assisted revision, on behalf of the editor named in X-OV-Subject. AI-generated revisions cannot be published without an approving review by a person.",
"tags": [
"tags": [
+ "wiki.review",
"wiki.revision"
"wiki.revision"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
+ "wiki.review.ai_editor",
"wiki.revision.publish"
"wiki.revision.publish"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
@@ -849,6 +851,11 @@
@@ -849,6 +851,11 @@
],
],
"x-openvibe-quota-class": "wiki-write",
"x-openvibe-quota-class": "wiki-write",
"security": [
"security": [
+ {
+ "openvibeToken": [
+ "wiki.review.ai_editor"
+ ]
+ },
{
{
"openvibeToken": [
"openvibeToken": [
"wiki.revision.publish"
"wiki.revision.publish"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Work API",
"title": "OpenVibe.Work API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -591,10 +591,992 @@
@@ -591,10 +591,992 @@
}
}
}
}
}
}
+ },
+ "/internal/config": {
+ "get": {
+ "operationId": "get_internal_config",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",
+ "description": "**work.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "work.config"
+ ],
+ "x-openvibe-capabilities": [
+ "work.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "work.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}": {
+ "get": {
+ "operationId": "get_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",
+ "description": "**work.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "work.config"
+ ],
+ "x-openvibe-capabilities": [
+ "work.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "work.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_internal_config_namespace",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",
+ "description": "**work.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "work.config"
+ ],
+ "x-openvibe-capabilities": [
+ "work.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "work.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/secrets/{key}": {
+ "delete": {
+ "operationId": "delete_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",
+ "description": "**work.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "work.config"
+ ],
+ "x-openvibe-capabilities": [
+ "work.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "work.config.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "values",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused."
+ },
+ {
+ "name": "reason",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ {
+ "name": "merge",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "description": "Merge into the active values instead of replacing them."
+ },
+ {
+ "name": "unset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "description": "Keys to remove from the new revision."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_internal_config_namespace_secrets_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",
+ "description": "**work.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "work.config"
+ ],
+ "x-openvibe-capabilities": [
+ "work.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "work.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/config/{namespace}/test/{key}": {
+ "post": {
+ "operationId": "post_internal_config_namespace_test_key",
+ "summary": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists th",
+ "description": "**work.config.manage** (internal): Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "tags": [
+ "work.config"
+ ],
+ "x-openvibe-capabilities": [
+ "work.config.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "config-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "work.config.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-change.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "key",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.config-namespace.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
}
}
},
},
"components": {
"components": {
"schemas": {
"schemas": {
+ "common.config-change.v1": {
+ "title": "ConfigChange",
+ "description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "values",
+ "reason"
+ ],
+ "properties": {
+ "values": {
+ "type": "object",
+ "description": "The new values, one member per key. The store validates them against the namespace schema; a secret-class value here is refused.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value; a redaction marker keeps the value it stands for."
+ }
+ },
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why the change is made; it is the revision's reason in the history."
+ },
+ "merge": {
+ "type": "boolean",
+ "default": false,
+ "description": "Merge into the active values instead of replacing them."
+ },
+ "unset": {
+ "type": "array",
+ "maxItems": 256,
+ "uniqueItems": true,
+ "description": "Keys to remove from the new revision.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ }
+ }
+ },
+ "common.config-namespace.v1": {
+ "title": "ConfigNamespace",
+ "description": "common.config-namespace@1 (AI Exchange D11): one service configuration namespace as openvibe-shared/config's summary() returns it (config-pg.js), plus `sealed`, `pins` and `schema`. GET /internal/config lists these for a service and GET /internal/config/:namespace returns one. Values are the active ones over the defaults, with every secret-class value replaced by its marker; a marker never carries the value. `sealed` says whether the service has a KEK so secrets are envelopes (false is the pre-D11 plaintext behaviour). `pins` names the keys whose x-env variable is set, so their effective value is the environment's and a write is 409 config.pinned_in_env. `schema` is the namespace's JSON Schema so a client can draw the form; its annotations are title, description, x-group, x-env, x-danger, x-bind and x-unit (nano_usd, ms, bps, seconds), and for the owner's Setup tab and the clear admin panel (openvibe-shared 4.125.0) x-owner-input ({ group, unlocks, get_it_at, get_it_how, test }: a field only the owner can give), x-required-for (the feature an unset field blocks), x-enum-labels ({ value: { label, description } }), x-advanced (folded under Advanced settings) and x-example (a placeholder; never on a secret).",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "values",
+ "classification",
+ "sealed",
+ "pins",
+ "schema"
+ ],
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. ai.exchange or live.site_settings."
+ },
+ "revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The active revision; null before the namespace has one."
+ },
+ "values": {
+ "type": "object",
+ "description": "The active values over the defaults, one member per key. A secret-class key's value is a marker, never the value.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a secret marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "$ref": "#/components/schemas/common.config-namespace.v1/$defs/marker"
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "sealed": {
+ "type": "boolean",
+ "description": "True when the service has a KEK: secret-class values are stored as envelopes and only opened for a caller that needs them. False keeps the pre-D11 plaintext-inside-values_json behaviour, and the tab shows it in red."
+ },
+ "pins": {
+ "type": "array",
+ "description": "The keys whose x-env variable is set in the service process: their effective value is the environment's, read-only, and a write answers 409 config.pinned_in_env.",
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ }
+ },
+ "schema": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The namespace's JSON Schema, or null when it has none. Annotations the client may read: title, description, x-group, x-env, x-danger, x-bind (the key whose value the secret is bound to), x-unit (nano_usd, ms, bps or seconds), x-owner-input, x-required-for, x-enum-labels, x-advanced and x-example."
+ },
+ "active": {
+ "description": "The active revision as a snapshot (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "last_known_good": {
+ "description": "The last revision that activated cleanly (common.config-snapshot@1), or null.",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/common.config-snapshot.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "$defs": {
+ "marker": {
+ "type": "object",
+ "description": "A secret-class value, as the summary and the secret endpoints show it: never the value. The fingerprint is keyed, so equal fingerprints within one namespace mean the value did not change.",
+ "additionalProperties": false,
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key."
+ },
+ "hint": {
+ "type": "string",
+ "minLength": 4,
+ "maxLength": 4,
+ "description": "The four-character hint (the value's last four characters), never more."
+ },
+ "set_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "set_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "bound_to": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2048,
+ "description": "The value of the key named by x-bind when the secret was set, e.g. a provider base_url origin."
+ }
+ }
+ }
+ }
+ },
+ "common.config-snapshot.v1": {
+ "title": "ConfigSnapshot",
+ "description": "One immutable revision of one service's configuration namespace (roadmap WS-C task 7; kept by openvibe-shared/config). A revision's values never change: revisions grow by one per namespace, and a rollback is a new revision that copies an older one's values. Only the state moves: proposed -> active -> superseded or rolled_back, or proposed -> rejected when validation or activation fails, in which case the previous active revision stays or is restored. A secret-class key's value never appears here: it is { redacted: true, fingerprint }, an HMAC-SHA256 of the value under a secret key the service keeps per namespace, so equal fingerprints within one namespace mean the value did not change, while the value cannot be recovered or guessed offline without that key. checksum is the sha256 of the canonical JSON (keys sorted, no whitespace) of values exactly as they appear here, secrets as their markers, so anyone can check it, and two snapshots of one namespace with the same checksum hold the same configuration.",
+ "type": "object",
+ "required": [
+ "service",
+ "namespace",
+ "revision",
+ "previous_revision",
+ "state",
+ "values",
+ "classification",
+ "checksum",
+ "created_at",
+ "created_by"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "namespace": {
+ "type": "string",
+ "maxLength": 120,
+ "pattern": "^[a-z][a-z0-9-]{1,39}(\\.[a-z0-9_-]+)+$",
+ "description": "<service>.<name>, e.g. live.site_settings or media.storage_tier."
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "previous_revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The revision that was active when this one was proposed; null for the namespace's first."
+ },
+ "state": {
+ "enum": [
+ "proposed",
+ "active",
+ "superseded",
+ "rejected",
+ "rolled_back"
+ ]
+ },
+ "values": {
+ "type": "object",
+ "description": "The configuration, one member per key. A secret-class key's value is a redaction marker.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "description": "Any JSON value. An object with a redacted member is a redaction marker, and nothing else.",
+ "anyOf": [
+ {
+ "type": [
+ "string",
+ "number",
+ "boolean",
+ "null",
+ "array"
+ ]
+ },
+ {
+ "type": "object",
+ "additionalProperties": true,
+ "not": {
+ "required": [
+ "redacted"
+ ]
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "redacted",
+ "fingerprint"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "redacted": {
+ "const": true
+ },
+ "fingerprint": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "HMAC-SHA256 (hex) of the canonical JSON of the value, keyed with the namespace's secret key: equal within one namespace when the value is unchanged, and not recoverable or guessable without the key."
+ }
+ },
+ "description": "A redacted secret-class value."
+ }
+ ]
+ }
+ },
+ "classification": {
+ "type": "object",
+ "description": "Every key of values, classified. A key the service does not classify is internal.",
+ "propertyNames": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$"
+ },
+ "additionalProperties": {
+ "enum": [
+ "public",
+ "internal",
+ "secret"
+ ]
+ }
+ },
+ "checksum": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$",
+ "description": "sha256 (hex) of the canonical JSON of values as they appear in this snapshot (secrets as their markers)."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "created_by": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "activated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "When it became active; null while proposed, and for a rejected revision."
+ },
+ "activated_by": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500
+ },
+ "error": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 2000,
+ "description": "Why it was rejected: the validation errors, or what activation reported. Never a secret value."
+ },
+ "copied_from": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "A rollback's source: the revision whose values this one copies."
+ }
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "const": "rejected"
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "error"
+ ],
+ "properties": {
+ "error": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "enum": [
+ "active",
+ "superseded",
+ "rolled_back"
+ ]
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "activated_at"
+ ],
+ "properties": {
+ "activated_at": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ ]
+ },
"errors.problem.v1": {
"errors.problem.v1": {
"title": "Problem",
"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.",
"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.",
@@ -661,6 +1643,123 @@
@@ -661,6 +1643,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."
+ }
+ ]
+ },
"work.application-result.v1": {
"work.application-result.v1": {
"title": "WorkApplicationResult",
"title": "WorkApplicationResult",
"description": "work.application-result@1: what work.application.manage answers: one application, or a page of them (GET /api/v1/me/applications, newest first, ?status= and ?cursor=; `next` is the next page's cursor or null). DELETE answers 204 with no body.",
"description": "work.application-result@1: what work.application.manage answers: one application, or a page of them (GET /api/v1/me/applications, newest first, ?status= and ?cursor=; `next` is the next page's cursor or null). DELETE answers 204 with no body.",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Zone API",
"title": "OpenVibe.Zone API",
- "version": "0.314.0",
- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.314.0): each operation lists the capabilities it performs; a token for 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.315.0",
+ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.315.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
new file mode 100644
new file mode 100644
@@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
+{
+ "id": "blog.review.ai_editor",
+ "version": "1.0",
+ "owner": "blog",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Record the editorial queue's AI editor decision (approved or rejected) on a post revision, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets an AI draft publish through the existing publish action and the post says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.",
+ "permissions": [
+ "blog:write"
+ ],
+ "resourceConstraints": [
+ "none"
+ ],
+ "quotaClass": "blog-write",
+ "events": [
+ "blog.post.updated",
+ "blog.index_document.upserted",
+ "blog.index_document.deleted"
+ ],
+ "implementedBy": [
+ "POST /api/v1/posts/:id/reviews"
+ ]
+}
@@ -2,9 +2,9 @@
@@ -2,9 +2,9 @@
"id": "chat.config.manage",
"id": "chat.config.manage",
"version": "1.0",
"version": "1.0",
"owner": "chat",
"owner": "chat",
- "status": "planned",
+ "status": "active",
"visibility": "internal",
"visibility": "internal",
- "description": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe.Chat mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "description": "Manage the configuration namespaces and secrets of OpenVibe.Chat through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
"permissions": [
"permissions": [
"config:manage"
"config:manage"
],
],
@@ -2,9 +2,9 @@
@@ -2,9 +2,9 @@
"id": "connect.config.manage",
"id": "connect.config.manage",
"version": "1.0",
"version": "1.0",
"owner": "connect",
"owner": "connect",
- "status": "planned",
+ "status": "active",
"visibility": "internal",
"visibility": "internal",
- "description": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe Connect mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "description": "Manage the configuration namespaces and secrets of OpenVibe Connect through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
"permissions": [
"permissions": [
"config:manage"
"config:manage"
],
],
@@ -2,9 +2,9 @@
@@ -2,9 +2,9 @@
"id": "deals.config.manage",
"id": "deals.config.manage",
"version": "1.0",
"version": "1.0",
"owner": "deals",
"owner": "deals",
- "status": "planned",
+ "status": "active",
"visibility": "internal",
"visibility": "internal",
- "description": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe.Deals mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "description": "Manage the configuration namespaces and secrets of OpenVibe.Deals through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
"permissions": [
"permissions": [
"config:manage"
"config:manage"
],
],
@@ -2,9 +2,9 @@
@@ -2,9 +2,9 @@
"id": "design.config.manage",
"id": "design.config.manage",
"version": "1.0",
"version": "1.0",
"owner": "design",
"owner": "design",
- "status": "planned",
+ "status": "active",
"visibility": "internal",
"visibility": "internal",
- "description": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe.Design mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "description": "Manage the configuration namespaces and secrets of OpenVibe.Design through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
"permissions": [
"permissions": [
"config:manage"
"config:manage"
],
],
@@ -2,9 +2,9 @@
@@ -2,9 +2,9 @@
"id": "domains.config.manage",
"id": "domains.config.manage",
"version": "1.0",
"version": "1.0",
"owner": "domains",
"owner": "domains",
- "status": "planned",
+ "status": "active",
"visibility": "internal",
"visibility": "internal",
- "description": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe.Domains mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "description": "Manage the configuration namespaces and secrets of OpenVibe.Domains through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
"permissions": [
"permissions": [
"config:manage"
"config:manage"
],
],
new file mode 100644
new file mode 100644
@@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
+{
+ "id": "news.review.ai_editor",
+ "version": "1.0",
+ "owner": "news",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Record the editorial queue's AI editor decision (approved or rejected) on a story revision or an AI brief, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets AI text publish and index and the page says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.",
+ "permissions": [
+ "news:write"
+ ],
+ "resourceConstraints": [
+ "none"
+ ],
+ "quotaClass": "news-write",
+ "events": [
+ "news.story.updated",
+ "news.index_document.upserted",
+ "news.index_document.deleted"
+ ],
+ "implementedBy": [
+ "POST /api/v1/stories/:id/reviews",
+ "POST /api/v1/briefs/:id/reviews"
+ ]
+}
@@ -2,9 +2,9 @@
@@ -2,9 +2,9 @@
"id": "sources.config.manage",
"id": "sources.config.manage",
"version": "1.0",
"version": "1.0",
"owner": "sources",
"owner": "sources",
- "status": "planned",
+ "status": "active",
"visibility": "internal",
"visibility": "internal",
- "description": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe.Sources mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "description": "Manage the configuration namespaces and secrets of OpenVibe.Sources through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
"permissions": [
"permissions": [
"config:manage"
"config:manage"
],
],
new file mode 100644
new file mode 100644
@@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
+{
+ "id": "wiki.review.ai_editor",
+ "version": "1.0",
+ "owner": "wiki",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Record the editorial queue's AI editor decision (approved or needs changes) on an existing revision, as the first-party service principal acting as itself (a svc: principal with no X-OV-Subject). This is never a person's review: an approval lets AI-written text publish and index, and the page says \"Checked by OpenVibe's AI editor\" with its date, never \"Reviewed by a person\". Granted only to the orchestrator's service principal.",
+ "permissions": [
+ "wiki:review:ai_editor"
+ ],
+ "resourceConstraints": [],
+ "quotaClass": "wiki-write",
+ "events": [
+ "wiki.page.updated",
+ "wiki.index_document.upserted",
+ "wiki.index_document.deleted"
+ ],
+ "implementedBy": [
+ "POST /api/v1/pages/:id/revisions/:n/review"
+ ]
+}
@@ -2,9 +2,9 @@
@@ -2,9 +2,9 @@
"id": "work.config.manage",
"id": "work.config.manage",
"version": "1.0",
"version": "1.0",
"owner": "work",
"owner": "work",
- "status": "planned",
+ "status": "active",
"visibility": "internal",
"visibility": "internal",
- "description": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Planned until OpenVibe.Work mounts these routes; it then turns active and its service manifest lists it. Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
+ "description": "Manage the configuration namespaces and secrets of OpenVibe.Work through openvibe-shared/config (D11), so the owner can give its keys and accounts on Network's Setup tab: GET /internal/config lists the namespace summaries, GET /internal/config/:namespace returns one as common.config-namespace@1, POST applies common.config-change@1, and PUT /internal/config/:namespace/secrets/:key sets a secret with common.config-secret-put@1 (write-only; the answer is the marker) while DELETE removes it. POST /internal/config/:namespace/test/:key asks the service's tester to try a stored value with the provider on the server, answering { ok, message } in one line that never carries a secret (the owner's Setup tab). Only Network's admin proxy, acting for the owner, holds this capability; it is never grantable to apps. Each request carries the owner as actor, and the service audits every write with that actor and key, never a secret value.",
"permissions": [
"permissions": [
"config:manage"
"config:manage"
],
],
@@ -53,6 +53,7 @@
@@ -53,6 +53,7 @@
"blog.post.schedule",
"blog.post.schedule",
"blog.post.unpublish",
"blog.post.unpublish",
"blog.post.delete",
"blog.post.delete",
+ "blog.review.ai_editor",
"blog.feed.read"
"blog.feed.read"
],
],
"eventsProduced": [
"eventsProduced": [
@@ -16,6 +16,7 @@
@@ -16,6 +16,7 @@
"chat.agent_poll.write",
"chat.agent_poll.write",
"chat.analysis.read",
"chat.analysis.read",
"chat.cache.invalidate",
"chat.cache.invalidate",
+ "chat.config.manage",
"chat.event.publish",
"chat.event.publish",
"chat.inbox.manage",
"chat.inbox.manage",
"chat.inbox.read",
"chat.inbox.read",
@@ -15,7 +15,9 @@
@@ -15,7 +15,9 @@
"state": "live",
"state": "live",
"publicSite": "service"
"publicSite": "service"
},
},
- "capabilities": [],
+ "capabilities": [
+ "connect.config.manage"
+ ],
"eventsProduced": ["connect.hook.received"],
"eventsProduced": ["connect.hook.received"],
"eventsConsumed": ["network.account.export_requested", "network.account.deleted", "connect.hook.received"],
"eventsConsumed": ["network.account.export_requested", "network.account.deleted", "connect.hook.received"],
"namespacesOwned": [
"namespacesOwned": [
@@ -78,6 +78,7 @@
@@ -78,6 +78,7 @@
"neighbourhood": "shop"
"neighbourhood": "shop"
},
},
"capabilities": [
"capabilities": [
+ "deals.config.manage",
"deals.offer.submit",
"deals.offer.submit",
"deals.offer.update",
"deals.offer.update",
"deals.offer.expire",
"deals.offer.expire",
@@ -15,7 +15,9 @@
@@ -15,7 +15,9 @@
"state": "live",
"state": "live",
"publicSite": "service"
"publicSite": "service"
},
},
- "capabilities": [],
+ "capabilities": [
+ "design.config.manage"
+ ],
"eventsProduced": [
"eventsProduced": [
"design.index_document.deleted",
"design.index_document.deleted",
"design.index_document.upserted"
"design.index_document.upserted"
@@ -17,6 +17,7 @@
@@ -17,6 +17,7 @@
},
},
"capabilities": [
"capabilities": [
"domains.actions.read",
"domains.actions.read",
+ "domains.config.manage",
"domains.proposal.decide"
"domains.proposal.decide"
],
],
"eventsProduced": [],
"eventsProduced": [],
@@ -88,6 +88,7 @@
@@ -88,6 +88,7 @@
"news.source.attach",
"news.source.attach",
"news.timeline.update",
"news.timeline.update",
"news.perspective.update",
"news.perspective.update",
+ "news.review.ai_editor",
"news.topic.manage"
"news.topic.manage"
],
],
"eventsProduced": [
"eventsProduced": [
@@ -20,6 +20,7 @@
@@ -20,6 +20,7 @@
"note": "internal ingestion service; sources.openvibe.network says so (its API is loopback-only)"
"note": "internal ingestion service; sources.openvibe.network says so (its API is loopback-only)"
},
},
"capabilities": [
"capabilities": [
+ "sources.config.manage",
"sources.source.read",
"sources.source.read",
"sources.item.read",
"sources.item.read",
"sources.source.manage"
"sources.source.manage"
@@ -45,6 +45,7 @@
@@ -45,6 +45,7 @@
"wiki.space.create",
"wiki.space.create",
"wiki.page.create",
"wiki.page.create",
"wiki.page.read",
"wiki.page.read",
+ "wiki.review.ai_editor",
"wiki.revision.propose",
"wiki.revision.propose",
"wiki.revision.publish",
"wiki.revision.publish",
"wiki.revision.revert",
"wiki.revision.revert",
@@ -16,7 +16,8 @@
@@ -16,7 +16,8 @@
"publicSite": "service"
"publicSite": "service"
},
},
"capabilities": [
"capabilities": [
- "work.application.manage"
+ "work.application.manage",
+ "work.config.manage"
],
],
"eventsProduced": [
"eventsProduced": [
"work.index_document.upserted",
"work.index_document.upserted",
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.311.0",
+ "version": "0.315.0",
"lockfileVersion": 3,
"lockfileVersion": 3,
"requires": true,
"requires": true,
"packages": {
"packages": {
"": {
"": {
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.311.0",
+ "version": "0.315.0",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.314.0",
+ "version": "0.315.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
"types": "generated/typescript/index.d.ts",
@@ -113,17 +113,16 @@ for (const cap of capabilities.manifests) {
@@ -113,17 +113,16 @@ for (const cap of capabilities.manifests) {
// openvibe-shared/config's admin routes. Only Network's admin proxy, acting for the owner, holds
// openvibe-shared/config's admin routes. Only Network's admin proxy, acting for the owner, holds
// them; they are never grantable to apps. Network uses its own admin config routes, not the proxy.
// them; they are never grantable to apps. Network uses its own admin config routes, not the proxy.
// 0.311.0 (the owner's Setup tab): the config admin routes gained POST /internal/config/:namespace/test/:key
// 0.311.0 (the owner's Setup tab): the config admin routes gained POST /internal/config/:namespace/test/:key
-// (openvibe-shared 4.125.0's test hook), and eight more services that hold an owner input are reachable. Those
-// eight are planned until each mounts the routes: then it turns active and its service manifest lists it (Deals'
-// own test pins that list, so listing it early would break the service's pin bump).
+// (openvibe-shared 4.125.0's test hook), and eight more services that hold an owner input are reachable. Seven of
+// them now mount the routes and are active and listed in their service manifest; Media is still planned until it does.
const CONFIG_ROUTES = ['GET /internal/config', 'GET|POST /internal/config/:namespace', 'PUT|DELETE /internal/config/:namespace/secrets/:key', 'POST /internal/config/:namespace/test/:key'];
const CONFIG_ROUTES = ['GET /internal/config', 'GET|POST /internal/config/:namespace', 'PUT|DELETE /internal/config/:namespace/secrets/:key', 'POST /internal/config/:namespace/test/:key'];
-for (const svc of ['ai', 'billing', 'services', 'watch', 'bench', 'report', 'host', 'codes', 'actor']) {
+for (const svc of ['ai', 'billing', 'services', 'watch', 'bench', 'report', 'host', 'codes', 'actor', 'chat', 'connect', 'deals', 'work', 'sources', 'design', 'domains']) {
const c = capabilities.get(`${svc}.config.manage`);
const c = capabilities.get(`${svc}.config.manage`);
ok(c && c.owner === svc && c.status === 'active' && c.visibility === 'internal', `${svc}.config.manage is an active internal capability`);
ok(c && c.owner === svc && c.status === 'active' && c.visibility === 'internal', `${svc}.config.manage is an active internal capability`);
ok(JSON.stringify(c.implementedBy) === JSON.stringify(CONFIG_ROUTES), `${svc}.config.manage names the config routes`);
ok(JSON.stringify(c.implementedBy) === JSON.stringify(CONFIG_ROUTES), `${svc}.config.manage names the config routes`);
ok(/Only Network's admin proxy, acting for the owner, holds this capability/.test(c.description) && /never grantable to apps/.test(c.description), `${svc}.config.manage says who holds it and that an app never does`);
ok(/Only Network's admin proxy, acting for the owner, holds this capability/.test(c.description) && /never grantable to apps/.test(c.description), `${svc}.config.manage says who holds it and that an app never does`);
}
}
-for (const svc of ['chat', 'connect', 'deals', 'work', 'sources', 'design', 'domains', 'media']) {
+for (const svc of ['media']) {
const c = capabilities.get(`${svc}.config.manage`);
const c = capabilities.get(`${svc}.config.manage`);
ok(c && c.owner === svc && c.status === 'planned' && c.visibility === 'internal', `${svc}.config.manage is a planned internal capability`);
ok(c && c.owner === svc && c.status === 'planned' && c.visibility === 'internal', `${svc}.config.manage is a planned internal capability`);
ok(JSON.stringify(c.implementedBy) === JSON.stringify(CONFIG_ROUTES), `${svc}.config.manage names the config routes`);
ok(JSON.stringify(c.implementedBy) === JSON.stringify(CONFIG_ROUTES), `${svc}.config.manage names the config routes`);