openvibers/openvibe.contracts Commits
Capability schemas (WS-C task 4): every remaining reference that did not match its routes is corrected. codes.release.manage, community pulse/space/thread reads and comment writes, sources item and source reads, ai run reads and creates, events publish (one envelope or a batch), identity.subject.resolve, network.modules.write, media object read and upload, search queries and the owner API, and tools registry reads and job create/read now name request and result schemas with one branch per route (29 new, fixtures for each). The Tools assertions in test/run.js name the new ids (unreleased)
Changes
@@@ -12,6 +12,21 @@ Every active capability now names both schemas: **the gap list is empty (162 →Seven older capability schema references are corrected to what the routes take and return. `events.event.read` now answers `events.read-result@1` (was the bare envelope). `codes.release.read` answers the new **`codes.release-read-result@1`** (was `codes.app-manifest@1`). `community.paste.create` answers the new **`community.paste-create-result@1`**. `community.post.create` answers the new **`community.post-write-result@1`**, built on the new **`community.forum-thread@1`** and **`community.forum-post@1`** records (both were `common.entity-ref@1`). `community.pulse.write` takes the new **`community.pulse-write-request@1`** (was `common.entity-ref@1`). `games.mod.manage` takes the new **`games.mod-manage-request@1`**, and `games.mod.read` answers the new **`games.mod-read-result@1`** built on the new **`games.mod-view@1`** (both were `mods.mod-manifest@1`). No contract changes shape; only the capability manifests point elsewhere. Additive.++The remaining references that did not match their routes are corrected the same way (29 new schemas):++- **codes.release.manage** takes **`codes.release-manage-request@1`** (was `codes.app-manifest@1`: the body wraps the manifest with kind, notes and publish; deprecate and revoke take a reason).++- **community.pulse.read**, **community.space.read** and **community.thread.read** answer **`community.pulse-read-result@1`**, **`community.space-read-result@1`** and **`community.thread-read-result@1`**, with the new **`community.pulse-item@1`** and **`community.forum-space@1`** records. Each also covers its HTML pages and RSS feeds. All three were `common.entity-ref@1`.++- **community.comment.write** takes **`community.comment-write-request@1`** and answers **`community.comment-write-result@1`**, with the new **`community.comment-thread@1`** and **`community.comment@1`** records. Both were `common.entity-ref@1`.++- **sources.item.read** and **sources.source.read** answer **`sources.item-read-result@1`** and **`sources.source-read-result@1`**: one branch per route, lists and single records.++- **ai.run.read** answers **`ai.run-read-result@1`**. **ai.run.create** takes **`ai.run-create-request@1`** (a run request, a direct operation's input, citations, or no body) and answers **`ai.run-create-result@1`** (`{ run }` or `{ citations }`, not the bare run), with the new **`ai.run-citation@1`**.++- **events.event.publish** and **events.app.publish** take **`events.publish-request@1`**: one envelope, or `{ events: [...] }` of 1 to 100.++- **identity.subject.resolve** takes **`identity.resolve-request@1`** and answers **`identity.resolve-result@1`** (**`identity.subject-projection@1`**, or `{ results }` for a batch). They were `identity.legacy-identity-map@1` and `identity.subject-ref@1`.++- **network.modules.write** takes **`network.module-write-request@1`** (`{ data }`; the answer stays `modules.module-record@1`).++- **media.object.read** takes no body and answers **`media.file-read-result@1`** (the file's metadata, or its bytes on `/f/:key`). **media.object.upload** answers the new **`media.file@1`** (Media answers a file record, not a `media.media-ref@1`).++- **search.query.run** and **search.query.delegate** answer **`search.read-result@1`**: search, suggestions, or one document. **search.document.write** takes **`search.document-write-request@1`** and answers **`search.owner-result@1`**: write results, reconciliation pages, the stored document, rejections.++- **tools.tool.read** answers **`tools.tool-read-result@1`** (the list, one tool, or its schema document). **tools.job.create** takes **`tools.job-create-request@1`**: retry and references take no body. **tools.job.read** answers **`tools.job-read-result@1`**: the job, or a result file's bytes. The Tools assertions in `test/run.js` name the new ids.++++No existing contract changes shape; only the manifests point elsewhere. Additive.++## 0.49.0 — 2026-09-25Platform blocks (roadmap WS-E task 5). **`network.block.changed`**: a person blocked or unblocked someone (`blocker`, `blocked`, `active`, a per-pair `revision`); Network keeps the blocks, keyed by subjects, and Chat and Community consume the event. New capability **`network.blocks.read`** (first-party, never for apps): `GET /internal/blocks?subject=` lists who a person blocked and who blocked them. Fixtures for a block and an unblock. Additive.nnew file mode 100644@@@ -0,0 +1,92 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/run-citation.v1.json",++ "title": "AiRunCitation",++ "description": "ai.run-citation@1: one source a run cited on OpenVibe.AI (the citations table): what the workflow was given or a citation the requester attached afterwards (provenance.attached), with trust and provenance.",++ "type": "object",++ "required": [++ "id",++ "run_id",++ "source_type"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "run_id": {++ "type": "string"++ },++ "ordinal": {++ "type": [++ "integer",++ "null"++ ]++ },++ "source_type": {++ "type": "string"++ },++ "source_id": {++ "type": [++ "string",++ "null"++ ]++ },++ "url": {++ "type": [++ "string",++ "null"++ ]++ },++ "title": {++ "type": [++ "string",++ "null"++ ]++ },++ "author": {++ "type": [++ "string",++ "null"++ ]++ },++ "published_at": {++ "type": [++ "string",++ "null"++ ]++ },++ "retrieved_at": {++ "type": [++ "string",++ "null"++ ]++ },++ "snippet": {++ "type": [++ "string",++ "null"++ ]++ },++ "content_hash": {++ "type": [++ "string",++ "null"++ ]++ },++ "trust": {++ "type": "object"++ },++ "provenance": {++ "type": "object"++ },++ "attached_by": {++ "type": [++ "string",++ "null"++ ]++ },++ "created_at": {++ "type": "string"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,77 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/run-create-request.v1.json",++ "title": "AiRunCreateRequest",++ "description": "ai.run-create-request@1: bodies of ai.run.create on OpenVibe.AI. POST /api/v1/runs: ai.run-request@1. POST /api/v1/chat, /generate, /summarize, /classify, /extract, /enrich and /embed: a run of workflow ai.<op> whose input is the body itself (validated by that workflow's own input schema), except idempotency_key, target, attribution, on_behalf_of and options, which are the run's. POST /api/v1/runs/:id/citations: { citations } (1-50, each with a source_type; the requester only). POST /api/v1/runs/:id/cancel and /retry take no body.",++ "anyOf": [++ {++ "$ref": "run-request.v1.json"++ },++ {++ "type": "object",++ "required": [++ "citations"++ ],++ "properties": {++ "citations": {++ "type": "array",++ "minItems": 1,++ "maxItems": 50,++ "items": {++ "type": "object",++ "required": [++ "source_type"++ ],++ "properties": {++ "source_type": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_.-]{1,63}$"++ },++ "source_id": {++ "type": "string",++ "maxLength": 4000++ },++ "url": {++ "type": "string",++ "maxLength": 4000++ },++ "title": {++ "type": "string",++ "maxLength": 4000++ },++ "author": {++ "type": "string",++ "maxLength": 4000++ },++ "snippet": {++ "type": "string",++ "maxLength": 4000++ },++ "provenance": {++ "type": "object"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "description": "A direct operation: the workflow input fields plus idempotency_key, target, attribution, on_behalf_of and options.",++ "properties": {++ "idempotency_key": {++ "type": "string"++ },++ "target": {},++ "attribution": {},++ "on_behalf_of": {},++ "options": {++ "type": "object"++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,36 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/run-create-result.v1.json",++ "title": "AiRunCreateResult",++ "description": "ai.run-create-result@1: answers of ai.run.create on OpenVibe.AI. POST /api/v1/runs and the direct operations → { run, replayed? } (201 when it finished or was served from cache within the wait, 202 with Location while queued or running, 200 with replayed true for an idempotent repeat); POST …/cancel → { run }; POST …/retry → { run } (a new run with retry_of); POST …/citations → 201 { citations }. A quota refusal or a full queue is 429 with Retry-After.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "run"++ ],++ "properties": {++ "run": {++ "$ref": "run.v1.json"++ },++ "replayed": {++ "const": true++ }++ }++ },++ {++ "type": "object",++ "required": [++ "citations"++ ],++ "properties": {++ "citations": {++ "type": "array",++ "items": {++ "$ref": "run-citation.v1.json"++ }++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,120 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/run-read-result.v1.json",++ "title": "AiRunReadResult",++ "description": "ai.run-read-result@1: answers of ai.run.read on OpenVibe.AI (the caller's own runs; usage readers may ask for all). GET /api/v1/runs?status=&workflow=&target=&limit=&before= → { runs }; GET /api/v1/runs/:id → { run, citations, requests } (the request log is metadata only: hashes, tokens, cost, latency, never prompts); GET /api/v1/runs/:id/citations → { citations }. Someone else's run is 404 run.not_found.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "runs"++ ],++ "properties": {++ "runs": {++ "type": "array",++ "items": {++ "$ref": "run.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "run",++ "citations",++ "requests"++ ],++ "properties": {++ "run": {++ "$ref": "run.v1.json"++ },++ "citations": {++ "type": "array",++ "items": {++ "$ref": "run-citation.v1.json"++ }++ },++ "requests": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "id",++ "seq",++ "status"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "seq": {++ "type": "integer"++ },++ "operation": {++ "type": [++ "string",++ "null"++ ]++ },++ "provider_key": {++ "type": [++ "string",++ "null"++ ]++ },++ "model_key": {++ "type": [++ "string",++ "null"++ ]++ },++ "status": {++ "type": "string"++ },++ "fallback": {},++ "tokens_in": {++ "type": [++ "integer",++ "null"++ ]++ },++ "tokens_out": {++ "type": [++ "integer",++ "null"++ ]++ },++ "cost_usd": {++ "type": [++ "number",++ "null"++ ]++ },++ "latency_ms": {++ "type": [++ "integer",++ "null"++ ]++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "citations"++ ],++ "properties": {++ "citations": {++ "type": "array",++ "items": {++ "$ref": "run-citation.v1.json"++ }++ }++ }++ }++ ]++}@@@ -31,6 +31,33 @@"status": "active","adr": "ADR-003"},++ {++ "id": "identity.resolve-request",++ "version": "1.0.0",++ "owner": "network",++ "schema": "identity/resolve-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "identity.resolve-result",++ "version": "1.0.0",++ "owner": "network",++ "schema": "identity/resolve-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "identity.subject-projection",++ "version": "1.0.0",++ "owner": "network",++ "schema": "identity/subject-projection.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "common.entity-ref","version": "1.0.0",@@@ -277,6 +304,15 @@"compatibility": "backward","status": "active"},++ {++ "id": "codes.release-manage-request",++ "version": "1.0.0",++ "owner": "codes",++ "schema": "codes/release-manage-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "events.redaction-directive","version": "1.0.0",@@@ -369,6 +405,15 @@"compatibility": "backward","status": "active"},++ {++ "id": "events.publish-request",++ "version": "1.0.0",++ "owner": "events",++ "schema": "events/publish-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "chat.message.deleted","version": "1.0.0",@@@ -457,6 +502,24 @@"compatibility": "backward","status": "active"},++ {++ "id": "sources.item-read-result",++ "version": "1.0.0",++ "owner": "sources",++ "schema": "sources/item-read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "sources.source-read-result",++ "version": "1.0.0",++ "owner": "sources",++ "schema": "sources/source-read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "search.document.indexed","version": "1.0.0",@@@ -477,6 +540,33 @@"status": "active","adr": "ADR-018"},++ {++ "id": "search.document-write-request",++ "version": "1.0.0",++ "owner": "search",++ "schema": "search/document-write-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "search.owner-result",++ "version": "1.0.0",++ "owner": "search",++ "schema": "search/owner-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "search.read-result",++ "version": "1.0.0",++ "owner": "search",++ "schema": "search/read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "wiki.space.updated","version": "1.0.0",@@@ -1149,6 +1239,42 @@"compatibility": "backward","status": "active"},++ {++ "id": "ai.run-citation",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/run-citation.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.run-create-request",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/run-create-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.run-create-result",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/run-create-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.run-read-result",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/run-read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "tips.interaction.ready","version": "1.0.0",@@@ -2259,6 +2385,15 @@"compatibility": "backward","status": "active"},++ {++ "id": "network.module-write-request",++ "version": "1.0.0",++ "owner": "network",++ "schema": "network/module-write-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "tools.tool","version": "1.1.0",@@@ -2319,6 +2454,33 @@"status": "active","adr": "ADR-027"},++ {++ "id": "tools.job-create-request",++ "version": "1.0.0",++ "owner": "tools",++ "schema": "tools/job-create-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "tools.job-read-result",++ "version": "1.0.0",++ "owner": "tools",++ "schema": "tools/job-read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "tools.tool-read-result",++ "version": "1.0.0",++ "owner": "tools",++ "schema": "tools/tool-read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "news.source.ingested","version": "1.0.0",@@@ -3687,6 +3849,24 @@"compatibility": "backward","status": "active"},++ {++ "id": "media.file-read-result",++ "version": "1.0.0",++ "owner": "media",++ "schema": "media/file-read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "media.file",++ "version": "1.0.0",++ "owner": "media",++ "schema": "media/file.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "community.paste.created","version": "1.0.0",@@@ -3978,6 +4158,87 @@"compatibility": "backward","status": "active"},++ {++ "id": "community.comment-thread",++ "version": "1.0.0",++ "owner": "community",++ "schema": "community/comment-thread.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "community.comment-write-request",++ "version": "1.0.0",++ "owner": "community",++ "schema": "community/comment-write-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "community.comment-write-result",++ "version": "1.0.0",++ "owner": "community",++ "schema": "community/comment-write-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "community.comment",++ "version": "1.0.0",++ "owner": "community",++ "schema": "community/comment.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "community.forum-space",++ "version": "1.0.0",++ "owner": "community",++ "schema": "community/forum-space.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "community.pulse-item",++ "version": "1.0.0",++ "owner": "community",++ "schema": "community/pulse-item.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "community.pulse-read-result",++ "version": "1.0.0",++ "owner": "community",++ "schema": "community/pulse-read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "community.space-read-result",++ "version": "1.0.0",++ "owner": "community",++ "schema": "community/space-read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "community.thread-read-result",++ "version": "1.0.0",++ "owner": "community",++ "schema": "community/thread-read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "openre.destination","version": "1.0.0",nnew file mode 100644@@@ -0,0 +1,62 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/codes/release-manage-request.v1.json",++ "title": "CodesReleaseManageRequest",++ "description": "codes.release-manage-request@1: bodies of codes.release.manage on OpenVibe.Codes (an app token managing its own releases). POST /api/v1/apps/:app/releases: { kind?, manifest, notes?, publish? } — kind mod or app (anything else is app); manifest must validate as codes.app-manifest@1 for an app or mods.mod-manifest@1 for a mod (422 manifest.invalid with the validation otherwise; a version that already has a release is 409); notes are cut to 2000 characters; publish true publishes the draft at once. POST /api/v1/releases/:id/deprecate: { reason, replacement? } (a reason is required, cut to 500 characters; replacement must be a published release of the same app). POST /api/v1/releases/:id/revoke: { reason } (required). POST /api/v1/releases/:id/publish takes no body. Unknown fields are ignored.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "manifest"++ ],++ "properties": {++ "kind": {++ "type": "string",++ "description": "mod, or app (the default)."++ },++ "manifest": {++ "anyOf": [++ {++ "$ref": "app-manifest.v1.json"++ },++ {++ "$ref": "../mods/mod-manifest.v1.json"++ }++ ]++ },++ "notes": {++ "type": [++ "string",++ "null"++ ]++ },++ "publish": {++ "type": "boolean"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "reason"++ ],++ "properties": {++ "reason": {++ "type": "string",++ "minLength": 1,++ "pattern": "\\S"++ },++ "replacement": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^(rel_[0-9A-HJKMNP-TV-Z]{26})?$"++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,56 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/comment-thread.v1.json",++ "title": "CommunityCommentThread",++ "description": "community.comment-thread@1: one embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeThread): the entity it belongs to, its visibility and comment count. id is the sequential id for services and the access id (cth_…) for everyone else; a hidden thread shows moderators its count, others only that it is hidden.",++ "type": "object",++ "required": [++ "id",++ "access_id",++ "ref",++ "visibility",++ "comment_count"++ ],++ "properties": {++ "id": {++ "type": [++ "integer",++ "string"++ ]++ },++ "access_id": {++ "type": "string",++ "pattern": "^cth_"++ },++ "ref": {++ "$ref": "../common/entity-ref.v1.json"++ },++ "visibility": {++ "enum": [++ "public",++ "hidden",++ "locked"++ ]++ },++ "comment_count": {++ "type": [++ "integer",++ "null"++ ]++ },++ "created_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,67 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/comment-write-request.v1.json",++ "title": "CommunityCommentWriteRequest",++ "description": "community.comment-write-request@1: bodies of community.comment.write on OpenVibe.Community (people, anonymous people with a name where the thread allows it, and services acting as X-OV-Subject or as AI). POST /api/v1/comments/threads/resolve: { ref } — get or create the thread of an entity (browsers only for the allow-listed entity types; a label is taken only from a service). POST /api/v1/comments/threads/:id/comments: { message, parent_id?, anon_name? } (message trimmed, at most 5000 characters; a reply to a reply joins the top-level comment). POST /api/v1/comments/:commentId/votes: { value: 1 | -1 | 0 } (people). GET /api/v1/comments/threads/:id and DELETE /api/v1/comments/:commentId take no body. Unknown fields are ignored.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "ref"++ ],++ "properties": {++ "ref": {++ "$ref": "../common/entity-ref.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "message"++ ],++ "properties": {++ "message": {++ "type": "string",++ "minLength": 1,++ "maxLength": 5000,++ "pattern": "\\S"++ },++ "parent_id": {++ "type": [++ "integer",++ "string",++ "null"++ ]++ },++ "anon_name": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ },++ {++ "type": "object",++ "required": [++ "value"++ ],++ "properties": {++ "value": {++ "enum": [++ 1,++ -1,++ 0,++ "1",++ "-1",++ "0"++ ]++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,125 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/comment-write-result.v1.json",++ "title": "CommunityCommentWriteResult",++ "description": "community.comment-write-result@1: answers of community.comment.write on OpenVibe.Community. POST …/threads/resolve → { thread, created } (201 when created); GET …/threads/:id?after=&sort=&limit=&parent= → { thread, comments, next_cursor, viewer }; POST …/threads/:id/comments → 201 { comment }; DELETE /api/v1/comments/:commentId → { ok, id }; POST …/:commentId/votes → { comment_id, score, upvotes, downvotes, my_vote }. Refusals are problem+json (thread.locked, auth.required, community.blocked, …).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "thread",++ "created"++ ],++ "properties": {++ "thread": {++ "$ref": "comment-thread.v1.json"++ },++ "created": {++ "type": "boolean"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "thread",++ "comments",++ "next_cursor",++ "viewer"++ ],++ "properties": {++ "thread": {++ "$ref": "comment-thread.v1.json"++ },++ "comments": {++ "type": "array",++ "items": {++ "$ref": "comment.v1.json"++ }++ },++ "next_cursor": {++ "type": [++ "string",++ "null"++ ]++ },++ "viewer": {++ "type": "object",++ "properties": {++ "signed_in": {++ "type": "boolean"++ },++ "can_comment": {++ "type": "boolean"++ },++ "can_vote": {++ "type": "boolean"++ },++ "can_moderate": {++ "type": "boolean"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "comment"++ ],++ "properties": {++ "comment": {++ "$ref": "comment.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "ok",++ "id"++ ],++ "properties": {++ "ok": {++ "const": true++ },++ "id": {++ "type": "integer"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "comment_id",++ "score",++ "upvotes",++ "downvotes",++ "my_vote"++ ],++ "properties": {++ "comment_id": {++ "type": "integer"++ },++ "score": {++ "type": "integer"++ },++ "upvotes": {++ "type": "integer",++ "minimum": 0++ },++ "downvotes": {++ "type": "integer",++ "minimum": 0++ },++ "my_vote": {++ "enum": [++ 1,++ -1,++ 0++ ]++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,163 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/comment.v1.json",++ "title": "CommunityComment",++ "description": "community.comment@1: one comment in an embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeComment), with its author (or anonymous name), score and the caller's vote, reply count and, for top-level comments in a page, the first replies. A deleted comment keeps its place with no author or message.",++ "type": "object",++ "required": [++ "id",++ "thread_id",++ "parent_id",++ "origin",++ "author",++ "message",++ "deleted",++ "score",++ "my_vote",++ "can_edit",++ "can_delete"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "thread_id": {++ "type": [++ "integer",++ "string"++ ]++ },++ "parent_id": {++ "type": [++ "integer",++ "null"++ ]++ },++ "origin": {++ "type": "string"++ },++ "author": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "subject": {++ "type": [++ "string",++ "null"++ ]++ },++ "username": {++ "type": [++ "string",++ "null"++ ]++ },++ "display_name": {++ "type": [++ "string",++ "null"++ ]++ },++ "avatar_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "profile_color": {++ "type": [++ "string",++ "null"++ ]++ },++ "is_ai": {++ "const": true++ },++ "is_system": {++ "const": true++ }++ }++ },++ "anon_name": {++ "type": [++ "string",++ "null"++ ]++ },++ "display_name": {++ "type": [++ "string",++ "null"++ ]++ },++ "message": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 5000++ },++ "deleted": {++ "type": "boolean"++ },++ "score": {++ "type": "integer"++ },++ "upvotes": {++ "type": "integer",++ "minimum": 0++ },++ "downvotes": {++ "type": "integer",++ "minimum": 0++ },++ "my_vote": {++ "enum": [++ 1,++ -1,++ 0++ ]++ },++ "reply_count": {++ "type": [++ "integer",++ "null"++ ]++ },++ "created_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "edited_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "can_edit": {++ "type": "boolean"++ },++ "can_delete": {++ "type": "boolean"++ },++ "replies": {++ "type": "array",++ "items": {++ "type": "object"++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,139 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/forum-space.v1.json",++ "title": "CommunityForumSpace",++ "description": "community.forum-space@1: one forum space as OpenVibe.Community's /api/v1/spaces API shows it (server/forum/service.js shapeSpace): its slug, name, visibility, thread kind and statuses, style, whether votes and reactions are on, its group and parent board, the members-only gate and, in lists, its counts and last activity.",++ "type": "object",++ "required": [++ "slug",++ "name",++ "visibility",++ "url",++ "thread_kind",++ "statuses",++ "style",++ "votes",++ "reactions",++ "group",++ "parent",++ "members_only"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "description": {++ "type": [++ "string",++ "null"++ ]++ },++ "visibility": {++ "enum": [++ "public",++ "members",++ "staff"++ ]++ },++ "url": {++ "type": "string"++ },++ "thread_kind": {++ "type": "string"++ },++ "statuses": {++ "type": "array",++ "items": {++ "type": "string"++ }++ },++ "style": {++ "enum": [++ "feed",++ "forum"++ ]++ },++ "votes": {++ "type": "boolean"++ },++ "reactions": {++ "type": "boolean"++ },++ "group": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ }++ }++ },++ "parent": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ }++ }++ },++ "post_count": {++ "type": "integer"++ },++ "thread_count": {++ "type": "integer"++ },++ "last_activity_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "members_only": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "owner": {++ "type": "string"++ },++ "owner_username": {++ "type": [++ "string",++ "null"++ ]++ },++ "join_url": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ },++ "last_post": {++ "type": [++ "object",++ "null"++ ]++ },++ "children": {++ "type": "array"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,113 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/pulse-item.v1.json",++ "title": "CommunityPulseItem",++ "description": "community.pulse-item@1: one item of the network's public activity on OpenVibe.Community (server/pulse/service.js shape), with provenance: its source entity, title, url, origin (user, ai or system), the AI or System label, the actor (a person for user items, never for AI or system ones) and when it happened.",++ "type": "object",++ "required": [++ "id",++ "source",++ "title",++ "url",++ "origin",++ "label",++ "actor",++ "occurred_at"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "source": {++ "type": "object",++ "required": [++ "service",++ "type",++ "id"++ ],++ "properties": {++ "service": {++ "type": "string"++ },++ "type": {++ "type": "string"++ },++ "id": {++ "type": "string"++ }++ }++ },++ "title": {++ "type": "string",++ "maxLength": 300++ },++ "url": {++ "type": "string"++ },++ "origin": {++ "enum": [++ "user",++ "ai",++ "system"++ ]++ },++ "label": {++ "enum": [++ "AI",++ "System",++ null++ ]++ },++ "actor": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "subject": {++ "type": [++ "string",++ "null"++ ]++ },++ "username": {++ "type": [++ "string",++ "null"++ ]++ },++ "display_name": {++ "type": [++ "string",++ "null"++ ]++ },++ "avatar_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "profile_color": {++ "type": [++ "string",++ "null"++ ]++ },++ "is_ai": {++ "const": true++ },++ "is_system": {++ "const": true++ }++ }++ },++ "occurred_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,35 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/pulse-read-result.v1.json",++ "title": "CommunityPulseReadResult",++ "description": "community.pulse-read-result@1: answers of community.pulse.read on OpenVibe.Community (anyone). GET /api/v1/pulse?origin=user|ai|system&after=&limit= → { items, next_cursor } (newest first, at most 100; an unknown origin or a cursor this API did not issue is 400). GET /pulse is the same activity as an HTML page.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "items",++ "next_cursor"++ ],++ "properties": {++ "items": {++ "type": "array",++ "maxItems": 100,++ "items": {++ "$ref": "pulse-item.v1.json"++ }++ },++ "next_cursor": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ },++ {++ "type": "string",++ "contentMediaType": "text/html",++ "description": "GET /pulse: the HTML page."++ }++ ]++}@@@ -6,46 +6,31 @@"anyOf": [{"type": "object",-- "required": ["item", "created"],++ "required": [++ "item",++ "created"++ ],"properties": {-- "created": { "type": "boolean" },++ "created": {++ "type": "boolean"++ },"item": {-- "type": "object",-- "required": ["id", "source", "title", "url", "origin", "label", "actor", "occurred_at"],-- "properties": {-- "id": { "type": "integer" },-- "source": {-- "type": "object",-- "required": ["service", "type", "id"],-- "properties": {-- "service": { "type": "string" },-- "type": { "type": "string" },-- "id": { "type": "string" }-- }-- },-- "title": { "type": "string", "maxLength": 300 },-- "url": { "type": "string" },-- "origin": { "enum": ["user", "ai", "system"] },-- "label": { "enum": ["AI", "System", null] },-- "actor": {-- "type": ["object", "null"],-- "properties": {-- "subject": { "type": ["string", "null"] },-- "username": { "type": ["string", "null"] },-- "display_name": { "type": ["string", "null"] },-- "avatar_url": { "type": ["string", "null"] },-- "profile_color": { "type": ["string", "null"] }-- }-- },-- "occurred_at": { "type": ["string", "null"] }-- }++ "$ref": "pulse-item.v1.json"}}},{"type": "object",-- "required": ["removed"],-- "properties": { "removed": { "type": "integer", "minimum": 0, "maximum": 1 } }++ "required": [++ "removed"++ ],++ "properties": {++ "removed": {++ "type": "integer",++ "minimum": 0,++ "maximum": 1++ }++ }}]}nnew file mode 100644@@@ -0,0 +1,70 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/space-read-result.v1.json",++ "title": "CommunitySpaceReadResult",++ "description": "community.space-read-result@1: answers of community.space.read on OpenVibe.Community. GET /api/v1/spaces → { spaces, groups } (the spaces the caller can open, each with its last post, and the board index: groups in order with their top-level spaces and child boards); GET /api/v1/spaces/:space → { space } (a staff or members-only space the caller cannot open is 404 or 401/403 vip.members_only); GET /s is the board index as an HTML page.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "spaces",++ "groups"++ ],++ "properties": {++ "spaces": {++ "type": "array",++ "items": {++ "$ref": "forum-space.v1.json"++ }++ },++ "groups": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "slug",++ "name",++ "spaces"++ ],++ "properties": {++ "slug": {++ "type": [++ "string",++ "null"++ ]++ },++ "name": {++ "type": "string"++ },++ "description": {++ "type": [++ "string",++ "null"++ ]++ },++ "spaces": {++ "type": "array"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "space"++ ],++ "properties": {++ "space": {++ "$ref": "forum-space.v1.json"++ }++ }++ },++ {++ "type": "string",++ "contentMediaType": "text/html",++ "description": "GET /s: the board index page."++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,134 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/thread-read-result.v1.json",++ "title": "CommunityThreadReadResult",++ "description": "community.thread-read-result@1: answers of community.thread.read on OpenVibe.Community. GET /api/v1/spaces/:space/threads?sort=&page=&limit=&category=&status= → a page of threads with the space, paging, categories, children and what the viewer may do; GET /api/v1/spaces/:space/threads/:slug?page= → the thread with a page of posts (reactions, author stats, crosspost info, attachments settings, paging, viewer). GET /s/:space and /s/:space/t/:slug are the same as HTML pages; GET /s/feed.xml and /s/:space/feed.xml are RSS feeds of public threads.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "space",++ "threads",++ "page",++ "pages",++ "total"++ ],++ "properties": {++ "space": {++ "type": "object"++ },++ "sort": {++ "type": "string"++ },++ "page": {++ "type": "integer",++ "minimum": 1++ },++ "per_page": {++ "type": "integer"++ },++ "total": {++ "type": "integer",++ "minimum": 0++ },++ "pages": {++ "type": "integer",++ "minimum": 1++ },++ "categories": {++ "type": "array"++ },++ "category": {++ "type": [++ "string",++ "null"++ ]++ },++ "status": {++ "type": [++ "string",++ "null"++ ]++ },++ "viewer": {++ "type": "object"++ },++ "children": {++ "type": "array"++ },++ "groups": {++ "type": "array"++ },++ "threads": {++ "type": "array",++ "items": {++ "$ref": "forum-thread.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "space",++ "thread",++ "posts",++ "page",++ "pages"++ ],++ "properties": {++ "space": {++ "type": "object"++ },++ "thread": {++ "$ref": "forum-thread.v1.json"++ },++ "posts": {++ "type": "array",++ "items": {++ "$ref": "forum-post.v1.json"++ }++ },++ "reactions": {++ "type": "array"++ },++ "crosspost": {++ "type": "object"++ },++ "categories": {++ "type": "array"++ },++ "attachments": {++ "type": "object"++ },++ "page": {++ "type": "integer",++ "minimum": 1++ },++ "per_page": {++ "type": "integer"++ },++ "pages": {++ "type": "integer",++ "minimum": 1++ },++ "total": {++ "type": "integer"++ },++ "viewer": {++ "type": "object"++ }++ }++ },++ {++ "type": "string",++ "contentMediaType": "text/html",++ "description": "GET /s/:space and /s/:space/t/:slug: HTML pages."++ },++ {++ "type": "string",++ "contentMediaType": "application/rss+xml",++ "description": "GET /s/feed.xml and /s/:space/feed.xml: RSS feeds."++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,32 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/publish-request.v1.json",++ "title": "EventsPublishRequest",++ "description": "events.publish-request@1: the body of POST /api/v1/events on OpenVibe.Events (events.event.publish for services, events.app.publish for developer apps): one events.event-envelope@1, or a batch { events: [...] } of 1 to 100 envelopes stored all or nothing. trace_id, priority (important) and visibility (internal) default when left out. source must be the caller and event_type one of its prefixes (an app: app-<ULID> and app.<project_key>.*, actor the app or the user it acts for).",++ "anyOf": [++ {++ "$ref": "event-envelope.v1.json"++ },++ {++ "type": "object",++ "required": [++ "events"++ ],++ "not": {++ "required": [++ "event_id"++ ]++ },++ "properties": {++ "events": {++ "type": "array",++ "minItems": 1,++ "maxItems": 100,++ "items": {++ "$ref": "event-envelope.v1.json"++ }++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,55 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/identity/resolve-request.v1.json",++ "title": "IdentityResolveRequest",++ "description": "identity.resolve-request@1: the request of identity.subject.resolve on OpenVibe.Network. GET /internal/identity/resolve?subject_id= or ?system=&id=[&type=user] takes no body. POST /internal/identity/resolve-batch: { subject_ids } or { system, type?, ids } (at most 500; 413 beyond).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "subject_ids"++ ],++ "properties": {++ "subject_ids": {++ "type": "array",++ "minItems": 1,++ "maxItems": 500,++ "items": {++ "type": "string"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "system",++ "ids"++ ],++ "properties": {++ "system": {++ "type": "string",++ "minLength": 1++ },++ "type": {++ "type": "string",++ "default": "user"++ },++ "ids": {++ "type": "array",++ "minItems": 1,++ "maxItems": 500,++ "items": {++ "type": [++ "string",++ "integer"++ ]++ }++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,32 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/identity/resolve-result.v1.json",++ "title": "IdentityResolveResult",++ "description": "identity.resolve-result@1: answers of identity.subject.resolve on OpenVibe.Network. GET /internal/identity/resolve → one identity.subject-projection@1 (404 identity.subject_not_found when nothing matches); POST /internal/identity/resolve-batch → { results } keyed by each id asked, null for an id that resolves to nothing.",++ "anyOf": [++ {++ "$ref": "subject-projection.v1.json"++ },++ {++ "type": "object",++ "required": [++ "results"++ ],++ "properties": {++ "results": {++ "type": "object",++ "additionalProperties": {++ "anyOf": [++ {++ "$ref": "subject-projection.v1.json"++ },++ {++ "type": "null"++ }++ ]++ }++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,62 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/identity/subject-projection.v1.json",++ "title": "IdentitySubjectProjection",++ "description": "identity.subject-projection@1: what OpenVibe.Network's identity resolver answers about a subject (server/identity/subjects.js resolve): the SubjectRef, the Network account behind it, its display fields, whether it is banned, and every legacy id mapped to it.",++ "type": "object",++ "required": [++ "subject",++ "username",++ "display_name",++ "avatar_url",++ "banned"++ ],++ "properties": {++ "subject": {++ "$ref": "subject-ref.v1.json"++ },++ "network_user_id": {++ "type": "integer"++ },++ "network_anon_id": {++ "type": "integer"++ },++ "username": {++ "type": "string"++ },++ "display_name": {++ "type": "string"++ },++ "avatar_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "banned": {++ "type": "boolean"++ },++ "legacy_ids": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "source_system",++ "source_type",++ "source_id"++ ],++ "properties": {++ "source_system": {++ "type": "string"++ },++ "source_type": {++ "type": "string"++ },++ "source_id": {++ "type": "string"++ }++ }++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/media/file-read-result.v1.json",++ "title": "MediaFileReadResult",++ "description": "media.file-read-result@1: answers of media.object.read on OpenVibe.Media. GET /api/v1/:app/files/:key → media.file@1 (the file's metadata; 404 when it is not the tenant's); GET /f/:key → the bytes, with Content-Type and Range (a sandbox file only through its signed URL).",++ "anyOf": [++ {++ "$ref": "file.v1.json"++ },++ {++ "type": "string",++ "contentEncoding": "binary",++ "description": "GET /f/:key: the file."++ }++ ]++}@@@ -2,11 +2,17 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/media/file-upload.v1.json","title": "MediaFileUpload",-- "description": "media.file-upload@1: the multipart/form-data body of POST /api/v1/:app/files (media.object.upload, namespace :app): one part named `file` with the bytes and its filename and type. The tenant's quota is checked against the bytes; the answer is media.media-ref@1.",++ "description": "media.file-upload@1: the multipart/form-data body of POST /api/v1/:app/files (media.object.upload, namespace :app): one part named `file` with the bytes and its filename and type. The tenant's quota is checked against the bytes; the answer is media.file@1 (201).","type": "object",-- "required": ["file"],++ "required": [++ "file"++ ],"additionalProperties": true,"properties": {-- "file": { "type": "string", "contentEncoding": "binary", "description": "The multipart part named file (filename and Content-Type from the part)." }++ "file": {++ "type": "string",++ "contentEncoding": "binary",++ "description": "The multipart part named file (filename and Content-Type from the part)."++ }}}nnew file mode 100644@@@ -0,0 +1,62 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/media/file.v1.json",++ "title": "MediaFile",++ "description": "media.file@1: one stored file of a tenant on OpenVibe.Media (server/files/routes.js filePublic): its key, owner, name, size, type and hash, and the URL it is served from (/f/<key>; a developer-project sandbox file gets a short-lived signed URL instead).",++ "type": "object",++ "required": [++ "key",++ "app_id",++ "original_name",++ "size",++ "mime",++ "url",++ "created_at"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "app_id": {++ "type": "string"++ },++ "user_id": {++ "type": [++ "string",++ "integer",++ "null"++ ]++ },++ "original_name": {++ "type": "string"++ },++ "size": {++ "type": "integer",++ "minimum": 0++ },++ "mime": {++ "type": "string"++ },++ "sha256": {++ "type": [++ "string",++ "null"++ ]++ },++ "url": {++ "type": "string"++ },++ "sandbox": {++ "const": true++ },++ "url_expires_at": {++ "type": [++ "string",++ "integer"++ ]++ },++ "created_at": {++ "type": "string"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/network/module-write-request.v1.json",++ "title": "NetworkModuleWriteRequest",++ "description": "network.module-write-request@1: the request of network.modules.write on OpenVibe.Network. PUT /internal/modules/:namespace/:subject: { data } — the record's new value, validated against the namespace's schema; If-Match names the revision read (412 if it moved). DELETE /internal/modules/:namespace/:subject (204) takes no body. The answer to PUT is modules.module-record@1.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "data"++ ],++ "properties": {++ "data": {++ "type": "object"++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,14 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/search/document-write-request.v1.json",++ "title": "SearchDocumentWriteRequest",++ "description": "search.document-write-request@1: the request of search.document.write on OpenVibe.Search (a service writes and reads only the documents it owns). PUT /api/v1/documents/:owner/:type/:id: a search.index-document@1 (owner, type and id come from the path; a body that names others is 422). DELETE /api/v1/documents/:owner/:type/:id?revision= (a tombstone) and the owner reads (GET /api/v1/owners/:owner/documents, …/documents/:type/:id, …/rejections) take no body.",++ "anyOf": [++ {++ "$ref": "index-document.v1.json"++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,175 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/search/owner-result.v1.json",++ "title": "SearchOwnerResult",++ "description": "search.owner-result@1: answers of search.document.write on OpenVibe.Search. PUT and DELETE /api/v1/documents/:owner/:type/:id → search.write-result@1 (stale and conflicting revisions are 409 problems); GET /api/v1/owners/:owner/documents?type=&after=&limit= → { owner, documents, next_after } (reconciliation: type, id, revision, deleted, exposure, hash, indexed_at); GET /api/v1/owners/:owner/documents/:type/:id → { document, effective_indexability, exposure, via, event_id, indexed_at } (the stored document); GET /api/v1/owners/:owner/rejections?after= → { rejections, next_after } (index-document events Search refused, at most 200).",++ "anyOf": [++ {++ "$ref": "write-result.v1.json"++ },++ {++ "type": "object",++ "required": [++ "owner",++ "documents",++ "next_after"++ ],++ "properties": {++ "owner": {++ "type": "string"++ },++ "documents": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "type",++ "id",++ "revision",++ "deleted"++ ],++ "properties": {++ "type": {++ "type": "string"++ },++ "id": {++ "type": "string"++ },++ "revision": {++ "type": "integer"++ },++ "deleted": {++ "type": "boolean"++ },++ "exposure": {},++ "hash": {++ "type": [++ "string",++ "null"++ ]++ },++ "indexed_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ },++ "next_after": {++ "type": [++ "integer",++ "null"++ ]++ }++ }++ },++ {++ "type": "object",++ "required": [++ "document",++ "effective_indexability"++ ],++ "properties": {++ "document": {++ "$ref": "index-document.v1.json"++ },++ "effective_indexability": {},++ "exposure": {},++ "via": {},++ "event_id": {++ "type": [++ "string",++ "null"++ ]++ },++ "indexed_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "rejections",++ "next_after"++ ],++ "properties": {++ "rejections": {++ "type": "array",++ "maxItems": 200,++ "items": {++ "type": "object",++ "required": [++ "seq",++ "code"++ ],++ "properties": {++ "seq": {++ "type": "integer"++ },++ "event_id": {++ "type": [++ "string",++ "null"++ ]++ },++ "event_type": {++ "type": [++ "string",++ "null"++ ]++ },++ "type": {++ "type": [++ "string",++ "null"++ ]++ },++ "id": {++ "type": [++ "string",++ "null"++ ]++ },++ "revision": {++ "type": [++ "integer",++ "null"++ ]++ },++ "code": {++ "type": "string"++ },++ "detail": {++ "type": [++ "string",++ "null"++ ]++ },++ "at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ },++ "next_after": {++ "type": [++ "integer",++ "null"++ ]++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,116 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/search/read-result.v1.json",++ "title": "SearchReadResult",++ "description": "search.read-result@1: answers of search.query.run and search.query.delegate on OpenVibe.Search. GET /api/v1/search → search.query-result@1; GET /api/v1/suggest?q=&owner=&type=&limit= → { suggestions } (title prefixes, at most 20, only documents the caller may see); GET /api/v1/documents/:owner/:type/:id → { document } (one document by exact id, if the caller may see it; 404 otherwise).",++ "anyOf": [++ {++ "$ref": "query-result.v1.json"++ },++ {++ "type": "object",++ "required": [++ "suggestions"++ ],++ "properties": {++ "suggestions": {++ "type": "array",++ "maxItems": 20,++ "items": {++ "type": "object",++ "required": [++ "owner",++ "type",++ "id"++ ],++ "properties": {++ "owner": {++ "type": "string"++ },++ "type": {++ "type": "string"++ },++ "id": {++ "type": "string"++ },++ "title": {++ "type": [++ "string",++ "null"++ ]++ },++ "canonical_url": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "document"++ ],++ "properties": {++ "document": {++ "type": "object",++ "required": [++ "owner",++ "type",++ "id",++ "revision",++ "indexable"++ ],++ "properties": {++ "owner": {++ "type": "string"++ },++ "type": {++ "type": "string"++ },++ "id": {++ "type": "string"++ },++ "revision": {++ "type": "integer"++ },++ "visibility": {++ "type": "string"++ },++ "title": {++ "type": [++ "string",++ "null"++ ]++ },++ "summary": {++ "type": [++ "string",++ "null"++ ]++ },++ "canonical_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "facets": {},++ "language": {},++ "authorship": {},++ "provenance": {},++ "published_at": {},++ "updated_at": {},++ "indexable": {++ "type": "boolean"++ }++ }++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,96 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/sources/item-read-result.v1.json",++ "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).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "items",++ "next_after",++ "sources"++ ],++ "properties": {++ "items": {++ "type": "array",++ "maxItems": 500,++ "items": {++ "$ref": "item.v1.json"++ }++ },++ "next_after": {++ "type": "integer",++ "minimum": 0++ },++ "more": {++ "type": "boolean"++ },++ "sources": {++ "type": "object",++ "additionalProperties": {++ "type": "object",++ "properties": {++ "status": {++ "type": "string"++ },++ "stale": {++ "type": "boolean"++ },++ "last_success_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "item",++ "source"++ ],++ "properties": {++ "item": {++ "$ref": "item.v1.json"++ },++ "source": {++ "anyOf": [++ {++ "type": "object",++ "required": [++ "key"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "status": {++ "type": "string"++ },++ "stale": {++ "type": "boolean"++ },++ "last_success_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ },++ {++ "type": "null"++ }++ ]++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,192 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/sources/source-read-result.v1.json",++ "title": "SourcesSourceReadResult",++ "description": "sources.source-read-result@1: answers of sources.source.read on OpenVibe.Sources. GET /api/v1/sources → { sources }; GET /api/v1/sources/:key → { source } (sources.source@1 with health); GET /api/v1/sources/:key/runs?before=&limit= → { runs, next_before } (newest first); GET /api/v1/runs?after=&source=&state=&limit= → { runs, next_after } (every source's fetch runs in order; state failed means any failure); GET /api/v1/health → { counts, attention } (sources by health status, and the failing, stale or never fetched ones).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "sources"++ ],++ "properties": {++ "sources": {++ "type": "array",++ "items": {++ "$ref": "source.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "source"++ ],++ "properties": {++ "source": {++ "$ref": "source.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "runs"++ ],++ "anyOf": [++ {++ "required": [++ "next_before"++ ]++ },++ {++ "required": [++ "next_after"++ ]++ }++ ],++ "properties": {++ "runs": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "source_key",++ "state"++ ],++ "properties": {++ "id": {++ "type": [++ "string",++ "integer",++ "null"++ ]++ },++ "source_key": {++ "type": "string"++ },++ "endpoint_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "trigger": {++ "type": "string"++ },++ "started_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "finished_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "state": {++ "type": "string"++ },++ "http_status": {++ "type": [++ "integer",++ "null"++ ]++ },++ "error_code": {++ "type": [++ "string",++ "null"++ ]++ },++ "detail": {++ "type": [++ "string",++ "null"++ ]++ },++ "bytes": {++ "type": [++ "integer",++ "null"++ ]++ },++ "raw_body_hash": {++ "type": [++ "string",++ "null"++ ]++ },++ "parser_version": {},++ "items": {++ "type": "object"++ },++ "seq": {++ "type": [++ "integer",++ "null"++ ]++ }++ }++ }++ },++ "next_before": {++ "type": [++ "integer",++ "null"++ ]++ },++ "next_after": {++ "type": [++ "integer",++ "null"++ ]++ }++ }++ },++ {++ "type": "object",++ "required": [++ "counts",++ "attention"++ ],++ "properties": {++ "counts": {++ "type": "object",++ "additionalProperties": {++ "type": "integer"++ }++ },++ "attention": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "key",++ "status"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "category": {++ "type": "string"++ },++ "type": {++ "type": "string"++ },++ "status": {++ "type": "string"++ }++ }++ }++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,14 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/tools/job-create-request.v1.json",++ "title": "ToolsJobCreateRequest",++ "description": "tools.job-create-request@1: the request of tools.job.create on the Tools satellites that run jobs. POST /api/v1/jobs: tools.job-request@1 (JSON or multipart). POST /api/v1/jobs/:id/retry and PUT|DELETE /api/v1/jobs/:id/references/:ref take no body.",++ "anyOf": [++ {++ "$ref": "job-request.v1.json"++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/tools/job-read-result.v1.json",++ "title": "ToolsJobReadResult",++ "description": "tools.job-read-result@1: answers of tools.job.read on the Tools satellites. GET /api/v1/jobs/:id → tools.job@1, and so is the data of every event on GET /api/v1/jobs/:id/events (Server-Sent Events, Last-Event-ID resume); GET /api/v1/jobs/:id/files/:n → the result file's bytes (Content-Type of the file; 409 tools.job.not_ready before the job succeeded).",++ "anyOf": [++ {++ "$ref": "job.v1.json"++ },++ {++ "type": "string",++ "contentEncoding": "binary",++ "description": "GET /api/v1/jobs/:id/files/:n."++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,48 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/tools/tool-read-result.v1.json",++ "title": "ToolsToolReadResult",++ "description": "tools.tool-read-result@1: answers of tools.tool.read on openvibe.tools (public registry; ADR-027). GET /api/v1/tools → tools.tool-list@1; GET /api/v1/tools/:id → tools.tool@1 with its schemas embedded; GET /api/v1/tools/:id/schema → the tool's own JSON Schema document { $schema, $id, $defs: { input, output } } (application/schema+json; input is false for a tool without an API input).",++ "anyOf": [++ {++ "$ref": "tool-list.v1.json"++ },++ {++ "$ref": "tool.v1.json"++ },++ {++ "type": "object",++ "required": [++ "$schema",++ "$id",++ "$defs"++ ],++ "properties": {++ "$schema": {++ "type": "string"++ },++ "$id": {++ "type": "string"++ },++ "$defs": {++ "type": "object",++ "required": [++ "input",++ "output"++ ],++ "properties": {++ "input": {++ "type": [++ "object",++ "boolean"++ ]++ },++ "output": {++ "type": "object"++ }++ }++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1 @@++{ "id": 1, "run_id": "r" }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": 55, "run_id": "run_01JAB2C3D4E5F6G7H8J9K0MNPS", "ordinal": 0, "source_type": "sources.item", "source_id": "itm_01JAB2C3D4E5F6G7H8J9K0MNPQ", "url": "https://news.example.com/a", "title": "A story", "author": null, "published_at": "2026-09-25", "retrieved_at": "2026-09-25T18:00:00Z", "snippet": null, "content_hash": null, "trust": {}, "provenance": {}, "attached_by": "workflow", "created_at": "2026-09-25T18:00:02.000Z" }nnew file mode 100644@@@ -0,0 +1 @@++[ "not", "an", "object" ]nnew file mode 100644@@@ -0,0 +1 @@++{ "citations": [ { "source_type": "wiki.page", "url": "https://openvibe.wiki/openvibe/restreaming", "title": "Restreaming" } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "text": "Summarize this stream recap.", "options": { "max_words": 80 }, "idempotency_key": "recap-812" }nnew file mode 100644@@@ -0,0 +1 @@++{ "workflow": "network.site_copy", "input": { "sites": [] } }nnew file mode 100644@@@ -0,0 +1 @@++{ "replayed": true }nnew file mode 100644@@@ -0,0 +1 @@++{ "run": { "id": "run_01JAB2C3D4E5F6G7H8J9K0MNPQ", "status": "succeeded", "workflow": { "key": "news.summarize_story", "version": 2 }, "template": { "key": "news.summarize_story", "version": 3 }, "route": { "key": "default.text", "version": 1 }, "requester": { "type": "service", "id": "news" }, "on_behalf_of": null, "attribution": null, "source_service": "news", "target": { "service": "news", "type": "story", "id": "sty_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "input": { "topic": "Transit strike", "sources": [{ "source_type": "news.item", "source_id": "itm_42" }] }, "output": { "headline": "Transit strike begins", "summary": "Workers walked out.", "citations": [0], "gaps": [] }, "error": null, "synthetic": false, "provenance": { "origin": "ai", "workflow": "news.summarize_story", "workflow_version": 2, "template_version": 3, "route": "default.text", "route_version": 1, "provider": "anthropic", "model": "claude-sonnet", "fallback_used": true, "run_id": "run_01JAB2C3D4E5F6G7H8J9K0MNPQ", "cached_from": null, "synthetic": false }, "usage": { "tokens_in": 1200, "tokens_out": 300, "cost_usd": 0.0081, "attempts": 2 }, "citations_count": 1, "retry_of": null, "idempotency_key": null, "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "created_at": "2026-09-23T10:00:00.000Z", "started_at": "2026-09-23T10:00:00.050Z", "finished_at": "2026-09-23T10:00:04.200Z"} }nnew file mode 100644@@@ -0,0 +1 @@++{ "runs": [ { "id": "run_1" } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "runs": [ { "id": "run_01JAB2C3D4E5F6G7H8J9K0MNPQ", "status": "succeeded", "workflow": { "key": "news.summarize_story", "version": 2 }, "template": { "key": "news.summarize_story", "version": 3 }, "route": { "key": "default.text", "version": 1 }, "requester": { "type": "service", "id": "news" }, "on_behalf_of": null, "attribution": null, "source_service": "news", "target": { "service": "news", "type": "story", "id": "sty_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "input": { "topic": "Transit strike", "sources": [{ "source_type": "news.item", "source_id": "itm_42" }] }, "output": { "headline": "Transit strike begins", "summary": "Workers walked out.", "citations": [0], "gaps": [] }, "error": null, "synthetic": false, "provenance": { "origin": "ai", "workflow": "news.summarize_story", "workflow_version": 2, "template_version": 3, "route": "default.text", "route_version": 1, "provider": "anthropic", "model": "claude-sonnet", "fallback_used": true, "run_id": "run_01JAB2C3D4E5F6G7H8J9K0MNPQ", "cached_from": null, "synthetic": false }, "usage": { "tokens_in": 1200, "tokens_out": 300, "cost_usd": 0.0081, "attempts": 2 }, "citations_count": 1, "retry_of": null, "idempotency_key": null, "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "created_at": "2026-09-23T10:00:00.000Z", "started_at": "2026-09-23T10:00:00.050Z", "finished_at": "2026-09-23T10:00:04.200Z"} ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "run": { "id": "run_01JAB2C3D4E5F6G7H8J9K0MNPQ", "status": "succeeded", "workflow": { "key": "news.summarize_story", "version": 2 }, "template": { "key": "news.summarize_story", "version": 3 }, "route": { "key": "default.text", "version": 1 }, "requester": { "type": "service", "id": "news" }, "on_behalf_of": null, "attribution": null, "source_service": "news", "target": { "service": "news", "type": "story", "id": "sty_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "input": { "topic": "Transit strike", "sources": [{ "source_type": "news.item", "source_id": "itm_42" }] }, "output": { "headline": "Transit strike begins", "summary": "Workers walked out.", "citations": [0], "gaps": [] }, "error": null, "synthetic": false, "provenance": { "origin": "ai", "workflow": "news.summarize_story", "workflow_version": 2, "template_version": 3, "route": "default.text", "route_version": 1, "provider": "anthropic", "model": "claude-sonnet", "fallback_used": true, "run_id": "run_01JAB2C3D4E5F6G7H8J9K0MNPQ", "cached_from": null, "synthetic": false }, "usage": { "tokens_in": 1200, "tokens_out": 300, "cost_usd": 0.0081, "attempts": 2 }, "citations_count": 1, "retry_of": null, "idempotency_key": null, "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "created_at": "2026-09-23T10:00:00.000Z", "started_at": "2026-09-23T10:00:00.050Z", "finished_at": "2026-09-23T10:00:04.200Z"}, "citations": [ { "id": 55, "run_id": "run_01JAB2C3D4E5F6G7H8J9K0MNPS", "ordinal": 0, "source_type": "sources.item", "source_id": "itm_01JAB2C3D4E5F6G7H8J9K0MNPQ", "url": "https://news.example.com/a", "title": "A story", "author": null, "published_at": "2026-09-25", "retrieved_at": "2026-09-25T18:00:00Z", "snippet": null, "content_hash": null, "trust": {}, "provenance": {}, "attached_by": "workflow", "created_at": "2026-09-25T18:00:02.000Z" } ], "requests": [ { "id": 7, "seq": 1, "operation": "chat", "provider_key": "anthropic", "model_key": "claude-sonnet-4-5", "status": "succeeded", "fallback": 0, "tokens_in": 812, "tokens_out": 140, "cost_usd": 0.0045, "latency_ms": 2310 } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "reason": " ", "manifest": 5 }nnew file mode 100644@@@ -0,0 +1 @@++{ "kind": "app", "manifest": { "id": "app_01JABCDEFGHJKMNPQRSTVWXYZ0", "name": "My app", "version": "0.1.0", "publisher": { "type": "app", "id": "app_01JABCDEFGHJKMNPQRSTVWXYZ0" }, "project_id": "prj_01JABCDEFGHJKMNPQRSTVWXYZ0", "environment": "sandbox", "capabilities": [], "compatibility": { "contracts": "^0.26.0", "sdk": "^0.2.2" }}, "notes": "First release.", "publish": true }nnew file mode 100644@@@ -0,0 +1 @@++{ "reason": "superseded by 1.5.0", "replacement": "rel_01JAB2C3D4E5F6G7H8J9K0MNPS" }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "access_id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "ref": { "service": "live", "type": "vod", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "visibility": "archived", "comment_count": 3, "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "access_id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "ref": { "service": "live", "type": "vod", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "visibility": "public", "comment_count": 3, "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "message": " ", "value": 3, "ref": "live:vod:1" }nnew file mode 100644@@@ -0,0 +1 @@++{ "message": "That ending!", "parent_id": 810 }nnew file mode 100644@@@ -0,0 +1 @@++{ "ref": { "service": "live", "type": "vod", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" } }nnew file mode 100644@@@ -0,0 +1 @@++{ "ok": false, "id": 1 }nnew file mode 100644@@@ -0,0 +1 @@++{ "thread": { "id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "access_id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "ref": { "service": "live", "type": "vod", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "visibility": "public", "comment_count": 3, "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z" }, "comments": [ { "id": 812, "thread_id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "parent_id": null, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": null }, "anon_name": null, "display_name": "Alice", "message": "That ending!", "deleted": false, "score": 2, "upvotes": 2, "downvotes": 0, "my_vote": 0, "reply_count": 0, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "edited_at": null, "can_edit": false, "can_delete": false } ], "next_cursor": null, "viewer": { "signed_in": true, "can_comment": true, "can_vote": true, "can_moderate": false } }nnew file mode 100644@@@ -0,0 +1 @@++{ "comment_id": 812, "score": 3, "upvotes": 3, "downvotes": 0, "my_vote": 1 }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": 812, "thread_id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "parent_id": null, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": null }, "anon_name": null, "display_name": "Alice", "message": "That ending!", "deleted": false, "score": 2, "upvotes": 2, "downvotes": 0, "my_vote": 5, "reply_count": 0, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "edited_at": null, "can_edit": false, "can_delete": false }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": 812, "thread_id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "parent_id": null, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": null }, "anon_name": null, "display_name": "Alice", "message": "That ending!", "deleted": false, "score": 2, "upvotes": 2, "downvotes": 0, "my_vote": 0, "reply_count": 0, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "edited_at": null, "can_edit": false, "can_delete": false }nnew file mode 100644@@@ -0,0 +1 @@++{ "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "wiki", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": 9001, "source": { "service": "live", "type": "clip", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "title": "Alice clipped a clean 1v3", "url": "https://openvibe.live/clip/med_01JAB2C3D4E5F6G7H8J9K0MNPQ", "origin": "bot", "label": "AI", "actor": null, "occurred_at": "2026-09-25T18:00:00.000Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": 9001, "source": { "service": "live", "type": "clip", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "title": "Alice clipped a clean 1v3", "url": "https://openvibe.live/clip/med_01JAB2C3D4E5F6G7H8J9K0MNPQ", "origin": "ai", "label": "AI", "actor": null, "occurred_at": "2026-09-25T18:00:00.000Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "items": "none" }nnew file mode 100644@@@ -0,0 +1 @@++"<!doctype html><title>Pulse</title>"nnew file mode 100644@@@ -0,0 +1 @@++{ "items": [ { "id": 9001, "source": { "service": "live", "type": "clip", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "title": "Alice clipped a clean 1v3", "url": "https://openvibe.live/clip/med_01JAB2C3D4E5F6G7H8J9K0MNPQ", "origin": "ai", "label": "AI", "actor": null, "occurred_at": "2026-09-25T18:00:00.000Z" } ], "next_cursor": "WyIyMDI2LTA5LTI1IDE4OjAwOjAwIiw5MDAxXQ" }nnew file mode 100644@@@ -0,0 +1 @@++{ "spaces": [ { "slug": "x" } ], "groups": [] }nnew file mode 100644@@@ -0,0 +1 @@++{ "spaces": [ { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" } ], "groups": [ { "slug": "make", "name": "Make things", "description": null, "spaces": [ { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" } ] } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "space": { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" } }nnew file mode 100644@@@ -0,0 +1 @@++{ "space": {}, "threads": [], "page": 0, "pages": 1, "total": 0 }nnew file mode 100644@@@ -0,0 +1 @@++"<?xml version=\"1.0\"?><rss version=\"2.0\"></rss>"nnew file mode 100644@@@ -0,0 +1 @@++{ "space": { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }, "thread": { "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/s/streaming/t/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 0 }, "posts": [ { "attachments": [], "pastes": [ { "slug": "k3JfQ2", "title": "ffmpeg restream flags", "language": "text", "type": "paste", "url": "/p/k3JfQ2", "screenshot_url": null, "lines": 1, "excerpt": "ffmpeg -re -i in.flv -c copy -f flv rtmp://example/live" } ], "id": 5120, "thread_id": 311, "is_opening": true, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "body_markdown": "What bitrates do you use?", "body_html": "<p>What bitrates do you use?</p>", "revision": 1, "deleted": false, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "can_edit": true } ], "reactions": [], "crosspost": { "from": null, "to": [], "targets": [] }, "categories": [], "attachments": { "enabled": true, "max": 4, "max_bytes": 8388608 }, "page": 1, "per_page": 20, "pages": 1, "total": 1, "viewer": { "signed_in": true } }nnew file mode 100644@@@ -0,0 +1 @@++{ "space": { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }, "sort": "hot", "page": 1, "per_page": 25, "total": 1, "pages": 1, "categories": [], "category": null, "status": null, "viewer": { "can_start": true, "can_moderate": false, "signed_in": true }, "children": [], "threads": [ { "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/s/streaming/t/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 0 } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "events": [] }nnew file mode 100644@@@ -0,0 +1 @@++{ "events": [ { "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "event_type": "media.vod.ready", "version": 1, "source": "media", "actor": { "type": "service", "id": "media" }, "timestamp": "2026-09-22T12:00:00Z", "priority": "important", "visibility": "internal", "subject": { "type": "vod", "id": "1203", "revision": 4 }, "payload": { "duration": 3600 }} ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "event_type": "media.vod.ready", "version": 1, "source": "media", "actor": { "type": "service", "id": "media" }, "timestamp": "2026-09-22T12:00:00Z", "priority": "important", "visibility": "internal", "subject": { "type": "vod", "id": "1203", "revision": 4 }, "payload": { "duration": 3600 }}nnew file mode 100644@@@ -0,0 +1 @@++{ "ids": ["17"] }nnew file mode 100644@@@ -0,0 +1 @@++{ "system": "live", "type": "user", "ids": ["17", 42] }nnew file mode 100644@@@ -0,0 +1 @@++{ "subject_ids": [ "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ" ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "results": { "x": 5 } }nnew file mode 100644@@@ -0,0 +1 @@++{ "results": { "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ": { "subject": { "type": "user", "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "network_user_id": 1042, "username": "alice", "display_name": "Alice", "avatar_url": null, "banned": false, "legacy_ids": [ { "source_system": "live", "source_type": "user", "source_id": "17" } ] }, "usr_01JAB2C3D4E5F6G7H8J9K0MNPR": null } }nnew file mode 100644@@@ -0,0 +1 @@++{ "subject": { "type": "user", "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "username": "alice", "display_name": "Alice", "avatar_url": null, "banned": "no" }nnew file mode 100644@@@ -0,0 +1 @@++{ "subject": { "type": "user", "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "network_user_id": 1042, "username": "alice", "display_name": "Alice", "avatar_url": null, "banned": false, "legacy_ids": [ { "source_system": "live", "source_type": "user", "source_id": "17" } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "key": "k" }nnew file mode 100644@@@ -0,0 +1 @@++"<bytes>"nnew file mode 100644@@@ -0,0 +1 @@++{ "key": "f_9c1b2e7d", "app_id": "live", "user_id": 17, "original_name": "overlay.png", "size": 48213, "mime": "image/png", "sha256": "3f1c0b8e2a9d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b", "url": "/f/f_9c1b2e7d", "created_at": "2026-09-25 18:00:00" }nnew file mode 100644@@@ -0,0 +1 @@++{ "key": "k", "app_id": "live", "original_name": "x", "size": -1, "mime": "image/png", "url": "/f/k", "created_at": "x" }nnew file mode 100644@@@ -0,0 +1 @@++{ "key": "f_9c1b2e7d", "app_id": "live", "user_id": 17, "original_name": "overlay.png", "size": 48213, "mime": "image/png", "sha256": "3f1c0b8e2a9d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b", "url": "/f/f_9c1b2e7d", "created_at": "2026-09-25 18:00:00" }nnew file mode 100644@@@ -0,0 +1 @@++{ "data": "on" }nnew file mode 100644@@@ -0,0 +1 @@++{ "data": { "send": true, "volume": 0.8 } }nnew file mode 100644@@@ -0,0 +1 @@++{ "owner": "Wiki!", "type": "page", "id": "x", "revision": -1 }nnew file mode 100644@@@ -0,0 +1 @@++{}nnew file mode 100644@@@ -0,0 +1 @@++{ "owner": "wiki", "type": "page", "id": "pg_01J0000000000000000000000Z", "revision": 3, "visibility": "public", "canonical_url": "https://openvibe.wiki/w/example", "title": "Example page", "summary": "A short summary.", "body": "Plain text body.", "facets": { "space": "general", "tags": [ "example" ] }, "language": "en", "authorship": "human", "publication_state": "published", "published_at": "2026-09-22T12:00:00Z", "indexability": { "decision": "index", "reasons": [] }, "provenance": [ { "service": "sources", "type": "item", "id": "itm_01J0000000000000000000000Z" } ]}nnew file mode 100644@@@ -0,0 +1 @@++{ "documents": [] }nnew file mode 100644@@@ -0,0 +1 @@++{ "outcome": "applied", "revision": 2, "stored_revision": 1, "deleted": false }nnew file mode 100644@@@ -0,0 +1 @@++{ "owner": "wiki", "documents": [ { "type": "page", "id": "restreaming", "revision": 3, "deleted": false, "exposure": "public", "hash": "ab12", "indexed_at": "2026-09-24T10:00:01.000Z" } ], "next_after": null }nnew file mode 100644@@@ -0,0 +1 @@++{ "rejections": [ { "seq": 4, "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "event_type": "wiki.index_document.upserted", "type": "page", "id": "x", "revision": 2, "code": "search.bad_document", "detail": "title is required", "at": "2026-09-25T18:00:00.000Z" } ], "next_after": 4 }nnew file mode 100644@@@ -0,0 +1 @@++{ "suggestions": "none" }nnew file mode 100644@@@ -0,0 +1 @@++{ "document": { "owner": "wiki", "type": "page", "id": "restreaming", "revision": 3, "visibility": "public", "title": "Restreaming", "summary": null, "canonical_url": "https://openvibe.wiki/openvibe/restreaming", "facets": {}, "language": "en", "authorship": "human", "provenance": [], "published_at": "2026-09-20T10:00:00Z", "updated_at": "2026-09-24T10:00:00Z", "indexable": true } }nnew file mode 100644@@@ -0,0 +1 @@++{ "results": [ { "owner": "live", "type": "vod", "id": "912", "revision": 3, "visibility": "public", "title": "Building a forum", "summary": null, "canonical_url": "https://openvibe.live/vod/912", "facets": { "channel": "alex" }, "language": null, "authorship": "human", "provenance": [], "published_at": "2026-09-24T19:00:00.000Z", "updated_at": null, "indexable": true, "snippet_html": "building a <mark>forum</mark>" } ], "next_cursor": null }nnew file mode 100644@@@ -0,0 +1 @@++{ "suggestions": [ { "owner": "wiki", "type": "page", "id": "restreaming", "title": "Restreaming", "canonical_url": "https://openvibe.wiki/openvibe/restreaming" } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "items": [], "sources": {} }nnew file mode 100644@@@ -0,0 +1 @@++{ "items": [ { "id": "itm_01J0000000000000000000000Z", "source_key": "nasa-news", "category": "news", "kind": "article", "identity": "https://www.nasa.gov/news-release/example/", "canonical_url": "https://www.nasa.gov/news-release/example/", "title": "Example release", "summary": null, "authors": [], "published_at": "2026-09-22T15:00:00Z", "source_updated_at": null, "fields": {}, "revision": 1, "provenance": { "retrieved_at": "2026-09-23T01:00:00Z", "first_seen_at": "2026-09-23T01:00:00Z", "content_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "raw_body_hash": null, "parser_version": "rss@1", "fetch_run_id": "frn_01J0000000000000000000000Z", "license_note": null, "terms_note": "US government work", "entered_by": null }, "removed": null} ], "next_after": 1204, "more": false, "sources": { "example-news": { "status": "healthy", "stale": false, "last_success_at": "2026-09-25T18:00:00.000Z" } } }nnew file mode 100644@@@ -0,0 +1 @@++{ "item": { "id": "itm_01J0000000000000000000000Z", "source_key": "nasa-news", "category": "news", "kind": "article", "identity": "https://www.nasa.gov/news-release/example/", "canonical_url": "https://www.nasa.gov/news-release/example/", "title": "Example release", "summary": null, "authors": [], "published_at": "2026-09-22T15:00:00Z", "source_updated_at": null, "fields": {}, "revision": 1, "provenance": { "retrieved_at": "2026-09-23T01:00:00Z", "first_seen_at": "2026-09-23T01:00:00Z", "content_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "raw_body_hash": null, "parser_version": "rss@1", "fetch_run_id": "frn_01J0000000000000000000000Z", "license_note": null, "terms_note": "US government work", "entered_by": null }, "removed": null}, "source": null }nnew file mode 100644@@@ -0,0 +1 @@++{ "runs": [] }nnew file mode 100644@@@ -0,0 +1 @@++{ "counts": { "healthy": 11, "stale": 1 }, "attention": [ { "key": "example-deals", "category": "deals", "type": "api", "status": "stale" } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "sources": [ { "key": "nasa-news", "name": "NASA news releases", "type": "rss", "category": "news", "homepage_url": "https://www.nasa.gov/", "endpoints": [{ "url": "https://www.nasa.gov/news-release/feed/" }], "auth": { "mode": "none" }, "robots_note": "robots.txt allows the feed path (checked 2026-09-23)", "terms_note": "US government work; verify before enabling", "license_note": null, "min_interval_ms": 60000, "poll_interval_sec": 3600, "stale_after_sec": 172800, "max_items": 200, "enabled": false, "review_required": true, "sensitivity": "none", "default_indexability": "noindex"} ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "runs": [ { "id": "run_1", "source_key": "example-news", "endpoint_url": "https://news.example.com/feed.xml", "trigger": "schedule", "started_at": "2026-09-25T18:00:00.000Z", "finished_at": "2026-09-25T18:00:01.000Z", "state": "ok", "http_status": 200, "error_code": null, "detail": null, "bytes": 18234, "raw_body_hash": null, "parser_version": "rss@1", "items": { "seen": 20, "created": 2 }, "seq": 991 } ], "next_after": 991 }nnew file mode 100644@@@ -0,0 +1 @@++"img.process"nnew file mode 100644@@@ -0,0 +1 @@++{}nnew file mode 100644@@@ -0,0 +1 @@++{ "type": "img.process", "input": { "tool": "convert", "format": "webp", "quality": 80 }}nnew file mode 100644@@@ -0,0 +1 @@++{ "id": 5 }nnew file mode 100644@@@ -0,0 +1 @@++"<bytes>"nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ", "object": "tools.job", "service": "img", "type": "img.process", "type_version": 1, "state": "queued", "progress": { "percent": null, "message": null }, "attempts": 0, "max_attempts": 3, "cancel_requested": false, "created_at": "2026-09-23T09:00:00.000Z", "started_at": null, "finished_at": null, "expires_at": "2026-09-23T10:00:00.000Z", "result": null, "error": null, "retryable": false, "retry_of": null, "retried_by": null, "references": [], "links": { "self": "/api/v1/jobs/job_01JAB2C3D4E5F6G7H8J9K0MNPQ", "events": "/api/v1/jobs/job_01JAB2C3D4E5F6G7H8J9K0MNPQ/events", "cancel": "/api/v1/jobs/job_01JAB2C3D4E5F6G7H8J9K0MNPQ", "retry": null, "retried_by": null }}nnew file mode 100644@@@ -0,0 +1 @@++{ "$schema": 5, "$defs": {} }nnew file mode 100644@@@ -0,0 +1 @@++{ "tools": [ { "id": "png", "family": "img", "name": "PNG Converter", "summary": "Convert JPG, WebP, AVIF, HEIC, GIF, BMP, TIFF and more to PNG.", "keywords": [ "jpg to png", "webp to png", "heic to png", "convert image to png" ], "status": "stable", "execution": "job", "api": true, "run": { "method": "POST", "path": "/api/v1/tools/png/run", "job": { "type": "img.process", "operation": "convert", "preset": { "format": "png" } }, "legacy": [ "POST /api/process", "POST /api/process/direct" ] }, "input": { "$ref": "https://openvibe.tools/api/v1/tools/png/schema#/$defs/input" }, "files": { "min": 1, "max": 1, "accept": [ "image/*" ], "maxBytes": 52428800 }, "output": { "kind": "file", "mime": [ "image/png" ] }, "limits": { "timeoutMs": 120000, "maxPixels": 100000000 }, "auth": { "anonymous": true, "capability": "tools.tool.run" }, "quotaClass": "tools-job", "cost": 5, "egress": false, "hosts": [ "png.openvibe.tools" ], "docs": "https://openvibe.tools/tool/png" }, { "id": "dns", "family": "net", "name": "DNS Lookup", "summary": "Look up A, AAAA, MX, TXT, NS, CNAME and other records for a domain.", "keywords": [ "dns lookup", "mx record lookup", "txt record check", "nslookup online" ], "status": "stable", "execution": "sync", "api": true, "run": { "method": "POST", "path": "/api/v1/tools/dns/run", "job": null, "legacy": [ "GET /api/net/dns/:target" ] }, "input": { "$ref": "https://openvibe.tools/api/v1/tools/dns/schema#/$defs/input" }, "files": null, "output": { "kind": "json", "schema": { "$ref": "https://openvibe.tools/api/v1/tools/dns/schema#/$defs/output" } }, "limits": { "timeoutMs": 10000, "maxInputBytes": 1024, "perTargetPerMinute": 30 }, "auth": { "anonymous": true, "capability": "tools.tool.run" }, "quotaClass": "tools-fetch", "cost": 1, "egress": true, "hosts": [ "dns.openvibe.tools" ], "docs": "https://openvibe.tools/tool/dns" }, { "id": "port", "family": "net", "name": "Port Checker", "summary": "Check whether TCP ports on a host are open.", "keywords": [ "open port checker", "port scanner" ], "status": "beta", "execution": "sync", "api": true, "run": { "method": "POST", "path": "/api/v1/tools/port/run", "job": null, "legacy": [ "GET /api/net/port/:target" ] }, "input": { "$ref": "https://openvibe.tools/api/v1/tools/port/schema#/$defs/input" }, "files": null, "output": { "kind": "json", "schema": { "$ref": "https://openvibe.tools/api/v1/tools/port/schema#/$defs/output" } }, "limits": { "timeoutMs": 15000, "perTargetPerMinute": 6 }, "auth": { "anonymous": false, "capability": "tools.net.probe" }, "quotaClass": "tools-probe", "cost": 5, "egress": true, "hosts": [ "port.openvibe.tools" ], "docs": "https://openvibe.tools/tool/port" }, { "id": "yt", "family": "media", "name": "YouTube Downloader", "summary": "Save a YouTube video or its audio.", "status": "stable", "execution": "job", "api": false, "run": null, "input": null, "files": null, "output": { "kind": "file", "mime": [ "video/mp4", "audio/mpeg" ] }, "limits": { "timeoutMs": 900000, "maxDurationSec": 3600, "perTargetPerMinute": 20 }, "auth": { "anonymous": true, "capability": "tools.tool.run" }, "quotaClass": "tools-download", "cost": 50, "egress": true, "hosts": [ "youtube-downloader.openvibe.tools", "yt.openvibe.tools" ], "docs": "https://openvibe.tools/tool/yt" } ], "count": 4, "updated_at": "2026-09-23T21:00:00.000Z", "families": [ { "id": "img", "name": "Image Tools", "count": 1 }, { "id": "net", "name": "Network Tools", "count": 2 }, { "id": "media", "name": "Media", "count": 1 } ]}nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "dns", "family": "net", "name": "DNS Lookup", "summary": "Look up A, AAAA, MX, TXT, NS, CNAME and other records for a domain.", "keywords": [ "dns lookup", "mx record lookup", "txt record check", "nslookup online" ], "status": "stable", "execution": "sync", "api": true, "run": { "method": "POST", "path": "/api/v1/tools/dns/run", "job": null, "legacy": [ "GET /api/net/dns/:target" ] }, "input": { "$ref": "https://openvibe.tools/api/v1/tools/dns/schema#/$defs/input" }, "files": null, "examples": [ { "title": "MX records of a domain", "input": { "target": "example.com", "type": "MX" } } ], "output": { "kind": "json", "schema": { "$ref": "https://openvibe.tools/api/v1/tools/dns/schema#/$defs/output" } }, "limits": { "timeoutMs": 10000, "maxInputBytes": 1024, "perTargetPerMinute": 30 }, "auth": { "anonymous": true, "capability": "tools.tool.run" }, "quotaClass": "tools-fetch", "cost": 1, "egress": true, "hosts": [ "dns.openvibe.tools" ], "docs": "https://openvibe.tools/tool/dns"}nnew file mode 100644@@@ -0,0 +1 @@++{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://openvibe.tools/api/v1/tools/dns/schema", "$defs": { "input": { "type": "object" }, "output": { "type": "object" } } }@@@ -272,6 +272,170 @@]}},++ "identity.resolve-request": {++ "version": "1.0.0",++ "owner": "network",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/identity/resolve-request.v1.json",++ "title": "IdentityResolveRequest",++ "description": "identity.resolve-request@1: the request of identity.subject.resolve on OpenVibe.Network. GET /internal/identity/resolve?subject_id= or ?system=&id=[&type=user] takes no body. POST /internal/identity/resolve-batch: { subject_ids } or { system, type?, ids } (at most 500; 413 beyond).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "subject_ids"++ ],++ "properties": {++ "subject_ids": {++ "type": "array",++ "minItems": 1,++ "maxItems": 500,++ "items": {++ "type": "string"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "system",++ "ids"++ ],++ "properties": {++ "system": {++ "type": "string",++ "minLength": 1++ },++ "type": {++ "type": "string",++ "default": "user"++ },++ "ids": {++ "type": "array",++ "minItems": 1,++ "maxItems": 500,++ "items": {++ "type": [++ "string",++ "integer"++ ]++ }++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "identity.resolve-result": {++ "version": "1.0.0",++ "owner": "network",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/identity/resolve-result.v1.json",++ "title": "IdentityResolveResult",++ "description": "identity.resolve-result@1: answers of identity.subject.resolve on OpenVibe.Network. GET /internal/identity/resolve → one identity.subject-projection@1 (404 identity.subject_not_found when nothing matches); POST /internal/identity/resolve-batch → { results } keyed by each id asked, null for an id that resolves to nothing.",++ "anyOf": [++ {++ "$ref": "subject-projection.v1.json"++ },++ {++ "type": "object",++ "required": [++ "results"++ ],++ "properties": {++ "results": {++ "type": "object",++ "additionalProperties": {++ "anyOf": [++ {++ "$ref": "subject-projection.v1.json"++ },++ {++ "type": "null"++ }++ ]++ }++ }++ }++ }++ ]++ }++ },++ "identity.subject-projection": {++ "version": "1.0.0",++ "owner": "network",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/identity/subject-projection.v1.json",++ "title": "IdentitySubjectProjection",++ "description": "identity.subject-projection@1: what OpenVibe.Network's identity resolver answers about a subject (server/identity/subjects.js resolve): the SubjectRef, the Network account behind it, its display fields, whether it is banned, and every legacy id mapped to it.",++ "type": "object",++ "required": [++ "subject",++ "username",++ "display_name",++ "avatar_url",++ "banned"++ ],++ "properties": {++ "subject": {++ "$ref": "subject-ref.v1.json"++ },++ "network_user_id": {++ "type": "integer"++ },++ "network_anon_id": {++ "type": "integer"++ },++ "username": {++ "type": "string"++ },++ "display_name": {++ "type": "string"++ },++ "avatar_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "banned": {++ "type": "boolean"++ },++ "legacy_ids": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "source_system",++ "source_type",++ "source_id"++ ],++ "properties": {++ "source_system": {++ "type": "string"++ },++ "source_type": {++ "type": "string"++ },++ "source_id": {++ "type": "string"++ }++ }++ }++ }++ }++ }++ },"common.entity-ref": {"version": "1.0.0","owner": "contracts",@@@ -1618,7 +1782,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/media/file-upload.v1.json","title": "MediaFileUpload",-- "description": "media.file-upload@1: the multipart/form-data body of POST /api/v1/:app/files (media.object.upload, namespace :app): one part named `file` with the bytes and its filename and type. The tenant's quota is checked against the bytes; the answer is media.media-ref@1.",++ "description": "media.file-upload@1: the multipart/form-data body of POST /api/v1/:app/files (media.object.upload, namespace :app): one part named `file` with the bytes and its filename and type. The tenant's quota is checked against the bytes; the answer is media.file@1 (201).","type": "object","required": ["file"@@@ -3576,6 +3740,73 @@]}},++ "codes.release-manage-request": {++ "version": "1.0.0",++ "owner": "codes",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/codes/release-manage-request.v1.json",++ "title": "CodesReleaseManageRequest",++ "description": "codes.release-manage-request@1: bodies of codes.release.manage on OpenVibe.Codes (an app token managing its own releases). POST /api/v1/apps/:app/releases: { kind?, manifest, notes?, publish? } — kind mod or app (anything else is app); manifest must validate as codes.app-manifest@1 for an app or mods.mod-manifest@1 for a mod (422 manifest.invalid with the validation otherwise; a version that already has a release is 409); notes are cut to 2000 characters; publish true publishes the draft at once. POST /api/v1/releases/:id/deprecate: { reason, replacement? } (a reason is required, cut to 500 characters; replacement must be a published release of the same app). POST /api/v1/releases/:id/revoke: { reason } (required). POST /api/v1/releases/:id/publish takes no body. Unknown fields are ignored.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "manifest"++ ],++ "properties": {++ "kind": {++ "type": "string",++ "description": "mod, or app (the default)."++ },++ "manifest": {++ "anyOf": [++ {++ "$ref": "app-manifest.v1.json"++ },++ {++ "$ref": "../mods/mod-manifest.v1.json"++ }++ ]++ },++ "notes": {++ "type": [++ "string",++ "null"++ ]++ },++ "publish": {++ "type": "boolean"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "reason"++ ],++ "properties": {++ "reason": {++ "type": "string",++ "minLength": 1,++ "pattern": "\\S"++ },++ "replacement": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^(rel_[0-9A-HJKMNP-TV-Z]{26})?$"++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },"events.redaction-directive": {"version": "1.0.0","owner": "events",@@@ -4235,6 +4466,43 @@}}},++ "events.publish-request": {++ "version": "1.0.0",++ "owner": "events",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/publish-request.v1.json",++ "title": "EventsPublishRequest",++ "description": "events.publish-request@1: the body of POST /api/v1/events on OpenVibe.Events (events.event.publish for services, events.app.publish for developer apps): one events.event-envelope@1, or a batch { events: [...] } of 1 to 100 envelopes stored all or nothing. trace_id, priority (important) and visibility (internal) default when left out. source must be the caller and event_type one of its prefixes (an app: app-<ULID> and app.<project_key>.*, actor the app or the user it acts for).",++ "anyOf": [++ {++ "$ref": "event-envelope.v1.json"++ },++ {++ "type": "object",++ "required": [++ "events"++ ],++ "not": {++ "required": [++ "event_id"++ ]++ },++ "properties": {++ "events": {++ "type": "array",++ "minItems": 1,++ "maxItems": 100,++ "items": {++ "$ref": "event-envelope.v1.json"++ }++ }++ }++ }++ ]++ }++ },"chat.message.deleted": {"version": "1.0.0","owner": "chat",@@@ -5070,6 +5338,304 @@]}},++ "sources.item-read-result": {++ "version": "1.0.0",++ "owner": "sources",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/sources/item-read-result.v1.json",++ "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).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "items",++ "next_after",++ "sources"++ ],++ "properties": {++ "items": {++ "type": "array",++ "maxItems": 500,++ "items": {++ "$ref": "item.v1.json"++ }++ },++ "next_after": {++ "type": "integer",++ "minimum": 0++ },++ "more": {++ "type": "boolean"++ },++ "sources": {++ "type": "object",++ "additionalProperties": {++ "type": "object",++ "properties": {++ "status": {++ "type": "string"++ },++ "stale": {++ "type": "boolean"++ },++ "last_success_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "item",++ "source"++ ],++ "properties": {++ "item": {++ "$ref": "item.v1.json"++ },++ "source": {++ "anyOf": [++ {++ "type": "object",++ "required": [++ "key"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "status": {++ "type": "string"++ },++ "stale": {++ "type": "boolean"++ },++ "last_success_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ },++ {++ "type": "null"++ }++ ]++ }++ }++ }++ ]++ }++ },++ "sources.source-read-result": {++ "version": "1.0.0",++ "owner": "sources",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/sources/source-read-result.v1.json",++ "title": "SourcesSourceReadResult",++ "description": "sources.source-read-result@1: answers of sources.source.read on OpenVibe.Sources. GET /api/v1/sources → { sources }; GET /api/v1/sources/:key → { source } (sources.source@1 with health); GET /api/v1/sources/:key/runs?before=&limit= → { runs, next_before } (newest first); GET /api/v1/runs?after=&source=&state=&limit= → { runs, next_after } (every source's fetch runs in order; state failed means any failure); GET /api/v1/health → { counts, attention } (sources by health status, and the failing, stale or never fetched ones).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "sources"++ ],++ "properties": {++ "sources": {++ "type": "array",++ "items": {++ "$ref": "source.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "source"++ ],++ "properties": {++ "source": {++ "$ref": "source.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "runs"++ ],++ "anyOf": [++ {++ "required": [++ "next_before"++ ]++ },++ {++ "required": [++ "next_after"++ ]++ }++ ],++ "properties": {++ "runs": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "source_key",++ "state"++ ],++ "properties": {++ "id": {++ "type": [++ "string",++ "integer",++ "null"++ ]++ },++ "source_key": {++ "type": "string"++ },++ "endpoint_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "trigger": {++ "type": "string"++ },++ "started_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "finished_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "state": {++ "type": "string"++ },++ "http_status": {++ "type": [++ "integer",++ "null"++ ]++ },++ "error_code": {++ "type": [++ "string",++ "null"++ ]++ },++ "detail": {++ "type": [++ "string",++ "null"++ ]++ },++ "bytes": {++ "type": [++ "integer",++ "null"++ ]++ },++ "raw_body_hash": {++ "type": [++ "string",++ "null"++ ]++ },++ "parser_version": {},++ "items": {++ "type": "object"++ },++ "seq": {++ "type": [++ "integer",++ "null"++ ]++ }++ }++ }++ },++ "next_before": {++ "type": [++ "integer",++ "null"++ ]++ },++ "next_after": {++ "type": [++ "integer",++ "null"++ ]++ }++ }++ },++ {++ "type": "object",++ "required": [++ "counts",++ "attention"++ ],++ "properties": {++ "counts": {++ "type": "object",++ "additionalProperties": {++ "type": "integer"++ }++ },++ "attention": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "key",++ "status"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "category": {++ "type": "string"++ },++ "type": {++ "type": "string"++ },++ "status": {++ "type": "string"++ }++ }++ }++ }++ }++ }++ ]++ }++ },"search.document.indexed": {"version": "1.0.0","owner": "search",@@@ -5202,6 +5768,326 @@}}},++ "search.document-write-request": {++ "version": "1.0.0",++ "owner": "search",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/search/document-write-request.v1.json",++ "title": "SearchDocumentWriteRequest",++ "description": "search.document-write-request@1: the request of search.document.write on OpenVibe.Search (a service writes and reads only the documents it owns). PUT /api/v1/documents/:owner/:type/:id: a search.index-document@1 (owner, type and id come from the path; a body that names others is 422). DELETE /api/v1/documents/:owner/:type/:id?revision= (a tombstone) and the owner reads (GET /api/v1/owners/:owner/documents, …/documents/:type/:id, …/rejections) take no body.",++ "anyOf": [++ {++ "$ref": "index-document.v1.json"++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "search.owner-result": {++ "version": "1.0.0",++ "owner": "search",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/search/owner-result.v1.json",++ "title": "SearchOwnerResult",++ "description": "search.owner-result@1: answers of search.document.write on OpenVibe.Search. PUT and DELETE /api/v1/documents/:owner/:type/:id → search.write-result@1 (stale and conflicting revisions are 409 problems); GET /api/v1/owners/:owner/documents?type=&after=&limit= → { owner, documents, next_after } (reconciliation: type, id, revision, deleted, exposure, hash, indexed_at); GET /api/v1/owners/:owner/documents/:type/:id → { document, effective_indexability, exposure, via, event_id, indexed_at } (the stored document); GET /api/v1/owners/:owner/rejections?after= → { rejections, next_after } (index-document events Search refused, at most 200).",++ "anyOf": [++ {++ "$ref": "write-result.v1.json"++ },++ {++ "type": "object",++ "required": [++ "owner",++ "documents",++ "next_after"++ ],++ "properties": {++ "owner": {++ "type": "string"++ },++ "documents": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "type",++ "id",++ "revision",++ "deleted"++ ],++ "properties": {++ "type": {++ "type": "string"++ },++ "id": {++ "type": "string"++ },++ "revision": {++ "type": "integer"++ },++ "deleted": {++ "type": "boolean"++ },++ "exposure": {},++ "hash": {++ "type": [++ "string",++ "null"++ ]++ },++ "indexed_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ },++ "next_after": {++ "type": [++ "integer",++ "null"++ ]++ }++ }++ },++ {++ "type": "object",++ "required": [++ "document",++ "effective_indexability"++ ],++ "properties": {++ "document": {++ "$ref": "index-document.v1.json"++ },++ "effective_indexability": {},++ "exposure": {},++ "via": {},++ "event_id": {++ "type": [++ "string",++ "null"++ ]++ },++ "indexed_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "rejections",++ "next_after"++ ],++ "properties": {++ "rejections": {++ "type": "array",++ "maxItems": 200,++ "items": {++ "type": "object",++ "required": [++ "seq",++ "code"++ ],++ "properties": {++ "seq": {++ "type": "integer"++ },++ "event_id": {++ "type": [++ "string",++ "null"++ ]++ },++ "event_type": {++ "type": [++ "string",++ "null"++ ]++ },++ "type": {++ "type": [++ "string",++ "null"++ ]++ },++ "id": {++ "type": [++ "string",++ "null"++ ]++ },++ "revision": {++ "type": [++ "integer",++ "null"++ ]++ },++ "code": {++ "type": "string"++ },++ "detail": {++ "type": [++ "string",++ "null"++ ]++ },++ "at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ },++ "next_after": {++ "type": [++ "integer",++ "null"++ ]++ }++ }++ }++ ]++ }++ },++ "search.read-result": {++ "version": "1.0.0",++ "owner": "search",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/search/read-result.v1.json",++ "title": "SearchReadResult",++ "description": "search.read-result@1: answers of search.query.run and search.query.delegate on OpenVibe.Search. GET /api/v1/search → search.query-result@1; GET /api/v1/suggest?q=&owner=&type=&limit= → { suggestions } (title prefixes, at most 20, only documents the caller may see); GET /api/v1/documents/:owner/:type/:id → { document } (one document by exact id, if the caller may see it; 404 otherwise).",++ "anyOf": [++ {++ "$ref": "query-result.v1.json"++ },++ {++ "type": "object",++ "required": [++ "suggestions"++ ],++ "properties": {++ "suggestions": {++ "type": "array",++ "maxItems": 20,++ "items": {++ "type": "object",++ "required": [++ "owner",++ "type",++ "id"++ ],++ "properties": {++ "owner": {++ "type": "string"++ },++ "type": {++ "type": "string"++ },++ "id": {++ "type": "string"++ },++ "title": {++ "type": [++ "string",++ "null"++ ]++ },++ "canonical_url": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "document"++ ],++ "properties": {++ "document": {++ "type": "object",++ "required": [++ "owner",++ "type",++ "id",++ "revision",++ "indexable"++ ],++ "properties": {++ "owner": {++ "type": "string"++ },++ "type": {++ "type": "string"++ },++ "id": {++ "type": "string"++ },++ "revision": {++ "type": "integer"++ },++ "visibility": {++ "type": "string"++ },++ "title": {++ "type": [++ "string",++ "null"++ ]++ },++ "summary": {++ "type": [++ "string",++ "null"++ ]++ },++ "canonical_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "facets": {},++ "language": {},++ "authorship": {},++ "provenance": {},++ "published_at": {},++ "updated_at": {},++ "indexable": {++ "type": "boolean"++ }++ }++ }++ }++ }++ ]++ }++ },"wiki.space.updated": {"version": "1.0.0","owner": "wiki",@@@ -12124,6 +13010,351 @@]}},++ "ai.run-citation": {++ "version": "1.0.0",++ "owner": "ai",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/run-citation.v1.json",++ "title": "AiRunCitation",++ "description": "ai.run-citation@1: one source a run cited on OpenVibe.AI (the citations table): what the workflow was given or a citation the requester attached afterwards (provenance.attached), with trust and provenance.",++ "type": "object",++ "required": [++ "id",++ "run_id",++ "source_type"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "run_id": {++ "type": "string"++ },++ "ordinal": {++ "type": [++ "integer",++ "null"++ ]++ },++ "source_type": {++ "type": "string"++ },++ "source_id": {++ "type": [++ "string",++ "null"++ ]++ },++ "url": {++ "type": [++ "string",++ "null"++ ]++ },++ "title": {++ "type": [++ "string",++ "null"++ ]++ },++ "author": {++ "type": [++ "string",++ "null"++ ]++ },++ "published_at": {++ "type": [++ "string",++ "null"++ ]++ },++ "retrieved_at": {++ "type": [++ "string",++ "null"++ ]++ },++ "snippet": {++ "type": [++ "string",++ "null"++ ]++ },++ "content_hash": {++ "type": [++ "string",++ "null"++ ]++ },++ "trust": {++ "type": "object"++ },++ "provenance": {++ "type": "object"++ },++ "attached_by": {++ "type": [++ "string",++ "null"++ ]++ },++ "created_at": {++ "type": "string"++ }++ }++ }++ },++ "ai.run-create-request": {++ "version": "1.0.0",++ "owner": "ai",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/run-create-request.v1.json",++ "title": "AiRunCreateRequest",++ "description": "ai.run-create-request@1: bodies of ai.run.create on OpenVibe.AI. POST /api/v1/runs: ai.run-request@1. POST /api/v1/chat, /generate, /summarize, /classify, /extract, /enrich and /embed: a run of workflow ai.<op> whose input is the body itself (validated by that workflow's own input schema), except idempotency_key, target, attribution, on_behalf_of and options, which are the run's. POST /api/v1/runs/:id/citations: { citations } (1-50, each with a source_type; the requester only). POST /api/v1/runs/:id/cancel and /retry take no body.",++ "anyOf": [++ {++ "$ref": "run-request.v1.json"++ },++ {++ "type": "object",++ "required": [++ "citations"++ ],++ "properties": {++ "citations": {++ "type": "array",++ "minItems": 1,++ "maxItems": 50,++ "items": {++ "type": "object",++ "required": [++ "source_type"++ ],++ "properties": {++ "source_type": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_.-]{1,63}$"++ },++ "source_id": {++ "type": "string",++ "maxLength": 4000++ },++ "url": {++ "type": "string",++ "maxLength": 4000++ },++ "title": {++ "type": "string",++ "maxLength": 4000++ },++ "author": {++ "type": "string",++ "maxLength": 4000++ },++ "snippet": {++ "type": "string",++ "maxLength": 4000++ },++ "provenance": {++ "type": "object"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "description": "A direct operation: the workflow input fields plus idempotency_key, target, attribution, on_behalf_of and options.",++ "properties": {++ "idempotency_key": {++ "type": "string"++ },++ "target": {},++ "attribution": {},++ "on_behalf_of": {},++ "options": {++ "type": "object"++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "ai.run-create-result": {++ "version": "1.0.0",++ "owner": "ai",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/run-create-result.v1.json",++ "title": "AiRunCreateResult",++ "description": "ai.run-create-result@1: answers of ai.run.create on OpenVibe.AI. POST /api/v1/runs and the direct operations → { run, replayed? } (201 when it finished or was served from cache within the wait, 202 with Location while queued or running, 200 with replayed true for an idempotent repeat); POST …/cancel → { run }; POST …/retry → { run } (a new run with retry_of); POST …/citations → 201 { citations }. A quota refusal or a full queue is 429 with Retry-After.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "run"++ ],++ "properties": {++ "run": {++ "$ref": "run.v1.json"++ },++ "replayed": {++ "const": true++ }++ }++ },++ {++ "type": "object",++ "required": [++ "citations"++ ],++ "properties": {++ "citations": {++ "type": "array",++ "items": {++ "$ref": "run-citation.v1.json"++ }++ }++ }++ }++ ]++ }++ },++ "ai.run-read-result": {++ "version": "1.0.0",++ "owner": "ai",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/run-read-result.v1.json",++ "title": "AiRunReadResult",++ "description": "ai.run-read-result@1: answers of ai.run.read on OpenVibe.AI (the caller's own runs; usage readers may ask for all). GET /api/v1/runs?status=&workflow=&target=&limit=&before= → { runs }; GET /api/v1/runs/:id → { run, citations, requests } (the request log is metadata only: hashes, tokens, cost, latency, never prompts); GET /api/v1/runs/:id/citations → { citations }. Someone else's run is 404 run.not_found.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "runs"++ ],++ "properties": {++ "runs": {++ "type": "array",++ "items": {++ "$ref": "run.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "run",++ "citations",++ "requests"++ ],++ "properties": {++ "run": {++ "$ref": "run.v1.json"++ },++ "citations": {++ "type": "array",++ "items": {++ "$ref": "run-citation.v1.json"++ }++ },++ "requests": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "id",++ "seq",++ "status"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "seq": {++ "type": "integer"++ },++ "operation": {++ "type": [++ "string",++ "null"++ ]++ },++ "provider_key": {++ "type": [++ "string",++ "null"++ ]++ },++ "model_key": {++ "type": [++ "string",++ "null"++ ]++ },++ "status": {++ "type": "string"++ },++ "fallback": {},++ "tokens_in": {++ "type": [++ "integer",++ "null"++ ]++ },++ "tokens_out": {++ "type": [++ "integer",++ "null"++ ]++ },++ "cost_usd": {++ "type": [++ "number",++ "null"++ ]++ },++ "latency_ms": {++ "type": [++ "integer",++ "null"++ ]++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "citations"++ ],++ "properties": {++ "citations": {++ "type": "array",++ "items": {++ "$ref": "run-citation.v1.json"++ }++ }++ }++ }++ ]++ }++ },"tips.interaction.ready": {"version": "1.0.0","owner": "tips",@@@ -23809,6 +25040,33 @@}}},++ "network.module-write-request": {++ "version": "1.0.0",++ "owner": "network",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/network/module-write-request.v1.json",++ "title": "NetworkModuleWriteRequest",++ "description": "network.module-write-request@1: the request of network.modules.write on OpenVibe.Network. PUT /internal/modules/:namespace/:subject: { data } — the record's new value, validated against the namespace's schema; If-Match names the revision read (412 if it moved). DELETE /internal/modules/:namespace/:subject (204) takes no body. The answer to PUT is modules.module-record@1.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "data"++ ],++ "properties": {++ "data": {++ "type": "object"++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },"tools.tool": {"version": "1.1.0","owner": "tools",@@@ -25253,6 +26511,99 @@}}},++ "tools.job-create-request": {++ "version": "1.0.0",++ "owner": "tools",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/tools/job-create-request.v1.json",++ "title": "ToolsJobCreateRequest",++ "description": "tools.job-create-request@1: the request of tools.job.create on the Tools satellites that run jobs. POST /api/v1/jobs: tools.job-request@1 (JSON or multipart). POST /api/v1/jobs/:id/retry and PUT|DELETE /api/v1/jobs/:id/references/:ref take no body.",++ "anyOf": [++ {++ "$ref": "job-request.v1.json"++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "tools.job-read-result": {++ "version": "1.0.0",++ "owner": "tools",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/tools/job-read-result.v1.json",++ "title": "ToolsJobReadResult",++ "description": "tools.job-read-result@1: answers of tools.job.read on the Tools satellites. GET /api/v1/jobs/:id → tools.job@1, and so is the data of every event on GET /api/v1/jobs/:id/events (Server-Sent Events, Last-Event-ID resume); GET /api/v1/jobs/:id/files/:n → the result file's bytes (Content-Type of the file; 409 tools.job.not_ready before the job succeeded).",++ "anyOf": [++ {++ "$ref": "job.v1.json"++ },++ {++ "type": "string",++ "contentEncoding": "binary",++ "description": "GET /api/v1/jobs/:id/files/:n."++ }++ ]++ }++ },++ "tools.tool-read-result": {++ "version": "1.0.0",++ "owner": "tools",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/tools/tool-read-result.v1.json",++ "title": "ToolsToolReadResult",++ "description": "tools.tool-read-result@1: answers of tools.tool.read on openvibe.tools (public registry; ADR-027). GET /api/v1/tools → tools.tool-list@1; GET /api/v1/tools/:id → tools.tool@1 with its schemas embedded; GET /api/v1/tools/:id/schema → the tool's own JSON Schema document { $schema, $id, $defs: { input, output } } (application/schema+json; input is false for a tool without an API input).",++ "anyOf": [++ {++ "$ref": "tool-list.v1.json"++ },++ {++ "$ref": "tool.v1.json"++ },++ {++ "type": "object",++ "required": [++ "$schema",++ "$id",++ "$defs"++ ],++ "properties": {++ "$schema": {++ "type": "string"++ },++ "$id": {++ "type": "string"++ },++ "$defs": {++ "type": "object",++ "required": [++ "input",++ "output"++ ],++ "properties": {++ "input": {++ "type": [++ "object",++ "boolean"++ ]++ },++ "output": {++ "type": "object"++ }++ }++ }++ }++ }++ ]++ }++ },"news.source.ingested": {"version": "1.0.0","owner": "news",@@@ -41825,6 +43176,94 @@}}},++ "media.file-read-result": {++ "version": "1.0.0",++ "owner": "media",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/media/file-read-result.v1.json",++ "title": "MediaFileReadResult",++ "description": "media.file-read-result@1: answers of media.object.read on OpenVibe.Media. GET /api/v1/:app/files/:key → media.file@1 (the file's metadata; 404 when it is not the tenant's); GET /f/:key → the bytes, with Content-Type and Range (a sandbox file only through its signed URL).",++ "anyOf": [++ {++ "$ref": "file.v1.json"++ },++ {++ "type": "string",++ "contentEncoding": "binary",++ "description": "GET /f/:key: the file."++ }++ ]++ }++ },++ "media.file": {++ "version": "1.0.0",++ "owner": "media",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/media/file.v1.json",++ "title": "MediaFile",++ "description": "media.file@1: one stored file of a tenant on OpenVibe.Media (server/files/routes.js filePublic): its key, owner, name, size, type and hash, and the URL it is served from (/f/<key>; a developer-project sandbox file gets a short-lived signed URL instead).",++ "type": "object",++ "required": [++ "key",++ "app_id",++ "original_name",++ "size",++ "mime",++ "url",++ "created_at"++ ],++ "properties": {++ "key": {++ "type": "string"++ },++ "app_id": {++ "type": "string"++ },++ "user_id": {++ "type": [++ "string",++ "integer",++ "null"++ ]++ },++ "original_name": {++ "type": "string"++ },++ "size": {++ "type": "integer",++ "minimum": 0++ },++ "mime": {++ "type": "string"++ },++ "sha256": {++ "type": [++ "string",++ "null"++ ]++ },++ "url": {++ "type": "string"++ },++ "sandbox": {++ "const": true++ },++ "url_expires_at": {++ "type": [++ "string",++ "integer"++ ]++ },++ "created_at": {++ "type": "string"++ }++ }++ }++ },"community.paste.created": {"version": "1.0.0","owner": "community",@@@ -45139,106 +46578,7 @@"type": "boolean"},"item": {-- "type": "object",-- "required": [-- "id",-- "source",-- "title",-- "url",-- "origin",-- "label",-- "actor",-- "occurred_at"-- ],-- "properties": {-- "id": {-- "type": "integer"-- },-- "source": {-- "type": "object",-- "required": [-- "service",-- "type",-- "id"-- ],-- "properties": {-- "service": {-- "type": "string"-- },-- "type": {-- "type": "string"-- },-- "id": {-- "type": "string"-- }-- }-- },-- "title": {-- "type": "string",-- "maxLength": 300-- },-- "url": {-- "type": "string"-- },-- "origin": {-- "enum": [-- "user",-- "ai",-- "system"-- ]-- },-- "label": {-- "enum": [-- "AI",-- "System",-- null-- ]-- },-- "actor": {-- "type": [-- "object",-- "null"-- ],-- "properties": {-- "subject": {-- "type": [-- "string",-- "null"-- ]-- },-- "username": {-- "type": [-- "string",-- "null"-- ]-- },-- "display_name": {-- "type": [-- "string",-- "null"-- ]-- },-- "avatar_url": {-- "type": [-- "string",-- "null"-- ]-- },-- "profile_color": {-- "type": [-- "string",-- "null"-- ]-- }-- }-- },-- "occurred_at": {-- "type": [-- "string",-- "null"-- ]-- }-- }++ "$ref": "pulse-item.v1.json"}}},@@@ -45896,6 +47236,953 @@]}},++ "community.comment-thread": {++ "version": "1.0.0",++ "owner": "community",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/comment-thread.v1.json",++ "title": "CommunityCommentThread",++ "description": "community.comment-thread@1: one embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeThread): the entity it belongs to, its visibility and comment count. id is the sequential id for services and the access id (cth_…) for everyone else; a hidden thread shows moderators its count, others only that it is hidden.",++ "type": "object",++ "required": [++ "id",++ "access_id",++ "ref",++ "visibility",++ "comment_count"++ ],++ "properties": {++ "id": {++ "type": [++ "integer",++ "string"++ ]++ },++ "access_id": {++ "type": "string",++ "pattern": "^cth_"++ },++ "ref": {++ "$ref": "../common/entity-ref.v1.json"++ },++ "visibility": {++ "enum": [++ "public",++ "hidden",++ "locked"++ ]++ },++ "comment_count": {++ "type": [++ "integer",++ "null"++ ]++ },++ "created_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ },++ "community.comment-write-request": {++ "version": "1.0.0",++ "owner": "community",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/comment-write-request.v1.json",++ "title": "CommunityCommentWriteRequest",++ "description": "community.comment-write-request@1: bodies of community.comment.write on OpenVibe.Community (people, anonymous people with a name where the thread allows it, and services acting as X-OV-Subject or as AI). POST /api/v1/comments/threads/resolve: { ref } — get or create the thread of an entity (browsers only for the allow-listed entity types; a label is taken only from a service). POST /api/v1/comments/threads/:id/comments: { message, parent_id?, anon_name? } (message trimmed, at most 5000 characters; a reply to a reply joins the top-level comment). POST /api/v1/comments/:commentId/votes: { value: 1 | -1 | 0 } (people). GET /api/v1/comments/threads/:id and DELETE /api/v1/comments/:commentId take no body. Unknown fields are ignored.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "ref"++ ],++ "properties": {++ "ref": {++ "$ref": "../common/entity-ref.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "message"++ ],++ "properties": {++ "message": {++ "type": "string",++ "minLength": 1,++ "maxLength": 5000,++ "pattern": "\\S"++ },++ "parent_id": {++ "type": [++ "integer",++ "string",++ "null"++ ]++ },++ "anon_name": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ },++ {++ "type": "object",++ "required": [++ "value"++ ],++ "properties": {++ "value": {++ "enum": [++ 1,++ -1,++ 0,++ "1",++ "-1",++ "0"++ ]++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "community.comment-write-result": {++ "version": "1.0.0",++ "owner": "community",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/comment-write-result.v1.json",++ "title": "CommunityCommentWriteResult",++ "description": "community.comment-write-result@1: answers of community.comment.write on OpenVibe.Community. POST …/threads/resolve → { thread, created } (201 when created); GET …/threads/:id?after=&sort=&limit=&parent= → { thread, comments, next_cursor, viewer }; POST …/threads/:id/comments → 201 { comment }; DELETE /api/v1/comments/:commentId → { ok, id }; POST …/:commentId/votes → { comment_id, score, upvotes, downvotes, my_vote }. Refusals are problem+json (thread.locked, auth.required, community.blocked, …).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "thread",++ "created"++ ],++ "properties": {++ "thread": {++ "$ref": "comment-thread.v1.json"++ },++ "created": {++ "type": "boolean"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "thread",++ "comments",++ "next_cursor",++ "viewer"++ ],++ "properties": {++ "thread": {++ "$ref": "comment-thread.v1.json"++ },++ "comments": {++ "type": "array",++ "items": {++ "$ref": "comment.v1.json"++ }++ },++ "next_cursor": {++ "type": [++ "string",++ "null"++ ]++ },++ "viewer": {++ "type": "object",++ "properties": {++ "signed_in": {++ "type": "boolean"++ },++ "can_comment": {++ "type": "boolean"++ },++ "can_vote": {++ "type": "boolean"++ },++ "can_moderate": {++ "type": "boolean"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "comment"++ ],++ "properties": {++ "comment": {++ "$ref": "comment.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "ok",++ "id"++ ],++ "properties": {++ "ok": {++ "const": true++ },++ "id": {++ "type": "integer"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "comment_id",++ "score",++ "upvotes",++ "downvotes",++ "my_vote"++ ],++ "properties": {++ "comment_id": {++ "type": "integer"++ },++ "score": {++ "type": "integer"++ },++ "upvotes": {++ "type": "integer",++ "minimum": 0++ },++ "downvotes": {++ "type": "integer",++ "minimum": 0++ },++ "my_vote": {++ "enum": [++ 1,++ -1,++ 0++ ]++ }++ }++ }++ ]++ }++ },++ "community.comment": {++ "version": "1.0.0",++ "owner": "community",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/comment.v1.json",++ "title": "CommunityComment",++ "description": "community.comment@1: one comment in an embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeComment), with its author (or anonymous name), score and the caller's vote, reply count and, for top-level comments in a page, the first replies. A deleted comment keeps its place with no author or message.",++ "type": "object",++ "required": [++ "id",++ "thread_id",++ "parent_id",++ "origin",++ "author",++ "message",++ "deleted",++ "score",++ "my_vote",++ "can_edit",++ "can_delete"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "thread_id": {++ "type": [++ "integer",++ "string"++ ]++ },++ "parent_id": {++ "type": [++ "integer",++ "null"++ ]++ },++ "origin": {++ "type": "string"++ },++ "author": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "subject": {++ "type": [++ "string",++ "null"++ ]++ },++ "username": {++ "type": [++ "string",++ "null"++ ]++ },++ "display_name": {++ "type": [++ "string",++ "null"++ ]++ },++ "avatar_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "profile_color": {++ "type": [++ "string",++ "null"++ ]++ },++ "is_ai": {++ "const": true++ },++ "is_system": {++ "const": true++ }++ }++ },++ "anon_name": {++ "type": [++ "string",++ "null"++ ]++ },++ "display_name": {++ "type": [++ "string",++ "null"++ ]++ },++ "message": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 5000++ },++ "deleted": {++ "type": "boolean"++ },++ "score": {++ "type": "integer"++ },++ "upvotes": {++ "type": "integer",++ "minimum": 0++ },++ "downvotes": {++ "type": "integer",++ "minimum": 0++ },++ "my_vote": {++ "enum": [++ 1,++ -1,++ 0++ ]++ },++ "reply_count": {++ "type": [++ "integer",++ "null"++ ]++ },++ "created_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "edited_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "can_edit": {++ "type": "boolean"++ },++ "can_delete": {++ "type": "boolean"++ },++ "replies": {++ "type": "array",++ "items": {++ "type": "object"++ }++ }++ }++ }++ },++ "community.forum-space": {++ "version": "1.0.0",++ "owner": "community",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/forum-space.v1.json",++ "title": "CommunityForumSpace",++ "description": "community.forum-space@1: one forum space as OpenVibe.Community's /api/v1/spaces API shows it (server/forum/service.js shapeSpace): its slug, name, visibility, thread kind and statuses, style, whether votes and reactions are on, its group and parent board, the members-only gate and, in lists, its counts and last activity.",++ "type": "object",++ "required": [++ "slug",++ "name",++ "visibility",++ "url",++ "thread_kind",++ "statuses",++ "style",++ "votes",++ "reactions",++ "group",++ "parent",++ "members_only"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "description": {++ "type": [++ "string",++ "null"++ ]++ },++ "visibility": {++ "enum": [++ "public",++ "members",++ "staff"++ ]++ },++ "url": {++ "type": "string"++ },++ "thread_kind": {++ "type": "string"++ },++ "statuses": {++ "type": "array",++ "items": {++ "type": "string"++ }++ },++ "style": {++ "enum": [++ "feed",++ "forum"++ ]++ },++ "votes": {++ "type": "boolean"++ },++ "reactions": {++ "type": "boolean"++ },++ "group": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ }++ }++ },++ "parent": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ }++ }++ },++ "post_count": {++ "type": "integer"++ },++ "thread_count": {++ "type": "integer"++ },++ "last_activity_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "members_only": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "owner": {++ "type": "string"++ },++ "owner_username": {++ "type": [++ "string",++ "null"++ ]++ },++ "join_url": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ },++ "last_post": {++ "type": [++ "object",++ "null"++ ]++ },++ "children": {++ "type": "array"++ }++ }++ }++ },++ "community.pulse-item": {++ "version": "1.0.0",++ "owner": "community",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/pulse-item.v1.json",++ "title": "CommunityPulseItem",++ "description": "community.pulse-item@1: one item of the network's public activity on OpenVibe.Community (server/pulse/service.js shape), with provenance: its source entity, title, url, origin (user, ai or system), the AI or System label, the actor (a person for user items, never for AI or system ones) and when it happened.",++ "type": "object",++ "required": [++ "id",++ "source",++ "title",++ "url",++ "origin",++ "label",++ "actor",++ "occurred_at"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "source": {++ "type": "object",++ "required": [++ "service",++ "type",++ "id"++ ],++ "properties": {++ "service": {++ "type": "string"++ },++ "type": {++ "type": "string"++ },++ "id": {++ "type": "string"++ }++ }++ },++ "title": {++ "type": "string",++ "maxLength": 300++ },++ "url": {++ "type": "string"++ },++ "origin": {++ "enum": [++ "user",++ "ai",++ "system"++ ]++ },++ "label": {++ "enum": [++ "AI",++ "System",++ null++ ]++ },++ "actor": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "subject": {++ "type": [++ "string",++ "null"++ ]++ },++ "username": {++ "type": [++ "string",++ "null"++ ]++ },++ "display_name": {++ "type": [++ "string",++ "null"++ ]++ },++ "avatar_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "profile_color": {++ "type": [++ "string",++ "null"++ ]++ },++ "is_ai": {++ "const": true++ },++ "is_system": {++ "const": true++ }++ }++ },++ "occurred_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ },++ "community.pulse-read-result": {++ "version": "1.0.0",++ "owner": "community",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/pulse-read-result.v1.json",++ "title": "CommunityPulseReadResult",++ "description": "community.pulse-read-result@1: answers of community.pulse.read on OpenVibe.Community (anyone). GET /api/v1/pulse?origin=user|ai|system&after=&limit= → { items, next_cursor } (newest first, at most 100; an unknown origin or a cursor this API did not issue is 400). GET /pulse is the same activity as an HTML page.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "items",++ "next_cursor"++ ],++ "properties": {++ "items": {++ "type": "array",++ "maxItems": 100,++ "items": {++ "$ref": "pulse-item.v1.json"++ }++ },++ "next_cursor": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ },++ {++ "type": "string",++ "contentMediaType": "text/html",++ "description": "GET /pulse: the HTML page."++ }++ ]++ }++ },++ "community.space-read-result": {++ "version": "1.0.0",++ "owner": "community",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/space-read-result.v1.json",++ "title": "CommunitySpaceReadResult",++ "description": "community.space-read-result@1: answers of community.space.read on OpenVibe.Community. GET /api/v1/spaces → { spaces, groups } (the spaces the caller can open, each with its last post, and the board index: groups in order with their top-level spaces and child boards); GET /api/v1/spaces/:space → { space } (a staff or members-only space the caller cannot open is 404 or 401/403 vip.members_only); GET /s is the board index as an HTML page.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "spaces",++ "groups"++ ],++ "properties": {++ "spaces": {++ "type": "array",++ "items": {++ "$ref": "forum-space.v1.json"++ }++ },++ "groups": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "slug",++ "name",++ "spaces"++ ],++ "properties": {++ "slug": {++ "type": [++ "string",++ "null"++ ]++ },++ "name": {++ "type": "string"++ },++ "description": {++ "type": [++ "string",++ "null"++ ]++ },++ "spaces": {++ "type": "array"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "space"++ ],++ "properties": {++ "space": {++ "$ref": "forum-space.v1.json"++ }++ }++ },++ {++ "type": "string",++ "contentMediaType": "text/html",++ "description": "GET /s: the board index page."++ }++ ]++ }++ },++ "community.thread-read-result": {++ "version": "1.0.0",++ "owner": "community",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/community/thread-read-result.v1.json",++ "title": "CommunityThreadReadResult",++ "description": "community.thread-read-result@1: answers of community.thread.read on OpenVibe.Community. GET /api/v1/spaces/:space/threads?sort=&page=&limit=&category=&status= → a page of threads with the space, paging, categories, children and what the viewer may do; GET /api/v1/spaces/:space/threads/:slug?page= → the thread with a page of posts (reactions, author stats, crosspost info, attachments settings, paging, viewer). GET /s/:space and /s/:space/t/:slug are the same as HTML pages; GET /s/feed.xml and /s/:space/feed.xml are RSS feeds of public threads.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "space",++ "threads",++ "page",++ "pages",++ "total"++ ],++ "properties": {++ "space": {++ "type": "object"++ },++ "sort": {++ "type": "string"++ },++ "page": {++ "type": "integer",++ "minimum": 1++ },++ "per_page": {++ "type": "integer"++ },++ "total": {++ "type": "integer",++ "minimum": 0++ },++ "pages": {++ "type": "integer",++ "minimum": 1++ },++ "categories": {++ "type": "array"++ },++ "category": {++ "type": [++ "string",++ "null"++ ]++ },++ "status": {++ "type": [++ "string",++ "null"++ ]++ },++ "viewer": {++ "type": "object"++ },++ "children": {++ "type": "array"++ },++ "groups": {++ "type": "array"++ },++ "threads": {++ "type": "array",++ "items": {++ "$ref": "forum-thread.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "space",++ "thread",++ "posts",++ "page",++ "pages"++ ],++ "properties": {++ "space": {++ "type": "object"++ },++ "thread": {++ "$ref": "forum-thread.v1.json"++ },++ "posts": {++ "type": "array",++ "items": {++ "$ref": "forum-post.v1.json"++ }++ },++ "reactions": {++ "type": "array"++ },++ "crosspost": {++ "type": "object"++ },++ "categories": {++ "type": "array"++ },++ "attachments": {++ "type": "object"++ },++ "page": {++ "type": "integer",++ "minimum": 1++ },++ "per_page": {++ "type": "integer"++ },++ "pages": {++ "type": "integer",++ "minimum": 1++ },++ "total": {++ "type": "integer"++ },++ "viewer": {++ "type": "object"++ }++ }++ },++ {++ "type": "string",++ "contentMediaType": "text/html",++ "description": "GET /s/:space and /s/:space/t/:slug: HTML pages."++ },++ {++ "type": "string",++ "contentMediaType": "application/rss+xml",++ "description": "GET /s/feed.xml and /s/:space/feed.xml: RSS feeds."++ }++ ]++ }++ },"openre.destination": {"version": "1.0.0","owner": "openre",@@@ -90,6 +90,60 @@ export type ServiceTokenClaims = {[k: string]: unknown | undefined;};++/** [email protected] (owner: network) */++/**++ * identity.resolve-request@1: the request of identity.subject.resolve on OpenVibe.Network. GET /internal/identity/resolve?subject_id= or ?system=&id=[&type=user] takes no body. POST /internal/identity/resolve-batch: { subject_ids } or { system, type?, ids } (at most 500; 413 beyond).++ */++export type IdentityResolveRequest =++ | {++ /**++ * @minItems 1++ * @maxItems 500++ */++ subject_ids: [string, ...string[]];++ }++ | {++ system: string;++ type?: string;++ /**++ * @minItems 1++ * @maxItems 500++ */++ ids: [string | number, ...(string | number)[]];++ }++ | NoBody;++++/** [email protected] (owner: network) */++/**++ * identity.resolve-result@1: answers of identity.subject.resolve on OpenVibe.Network. GET /internal/identity/resolve → one identity.subject-projection@1 (404 identity.subject_not_found when nothing matches); POST /internal/identity/resolve-batch → { results } keyed by each id asked, null for an id that resolves to nothing.++ */++export type IdentityResolveResult =++ | IdentitySubjectProjection++ | {++ results: {++ [k: string]: (IdentitySubjectProjection | null) | undefined;++ };++ };++++/** [email protected] (owner: network) */++/**++ * identity.subject-projection@1: what OpenVibe.Network's identity resolver answers about a subject (server/identity/subjects.js resolve): the SubjectRef, the Network account behind it, its display fields, whether it is banned, and every legacy id mapped to it.++ */++export interface IdentitySubjectProjection {++ subject: SubjectRef;++ network_user_id?: number;++ network_anon_id?: number;++ username: string;++ display_name: string;++ avatar_url: string | null;++ banned: boolean;++ legacy_ids?: {++ source_system: string;++ source_type: string;++ source_id: string;++ }[];++}++/** [email protected] (owner: contracts) *//*** Typed reference to another service's entity. Store this instead of a foreign key; resolve a display projection from the owner.@@@ -615,7 +669,7 @@ export type Binary = string;/** [email protected] (owner: media) *//**-- * media.file-upload@1: the multipart/form-data body of POST /api/v1/:app/files (media.object.upload, namespace :app): one part named `file` with the bytes and its filename and type. The tenant's quota is checked against the bytes; the answer is media.media-ref@1.++ * media.file-upload@1: the multipart/form-data body of POST /api/v1/:app/files (media.object.upload, namespace :app): one part named `file` with the bytes and its filename and type. The tenant's quota is checked against the bytes; the answer is media.file@1 (201).*/export interface MediaFileUpload {/**@@@ -5826,6 +5880,26 @@ export type CodesReleaseReadResult =manifest: AppManifest | ModManifest | null;};++/** [email protected] (owner: codes) */++/**++ * codes.release-manage-request@1: bodies of codes.release.manage on OpenVibe.Codes (an app token managing its own releases). POST /api/v1/apps/:app/releases: { kind?, manifest, notes?, publish? } — kind mod or app (anything else is app); manifest must validate as codes.app-manifest@1 for an app or mods.mod-manifest@1 for a mod (422 manifest.invalid with the validation otherwise; a version that already has a release is 409); notes are cut to 2000 characters; publish true publishes the draft at once. POST /api/v1/releases/:id/deprecate: { reason, replacement? } (a reason is required, cut to 500 characters; replacement must be a published release of the same app). POST /api/v1/releases/:id/revoke: { reason } (required). POST /api/v1/releases/:id/publish takes no body. Unknown fields are ignored.++ */++export type CodesReleaseManageRequest =++ | {++ /**++ * mod, or app (the default).++ */++ kind?: string;++ manifest: AppManifest | ModManifest;++ notes?: string | null;++ publish?: boolean;++ }++ | {++ reason: string;++ replacement?: string | null;++ }++ | NoBody;++/** [email protected] (owner: events) *//*** payload.redacts (ADR-026): a producer takes back events it published earlier. Any event may carry it, normally the producer's own *.deleted event. It names event_ids, or subject_type + subject_ids, or both, never an empty list. event_ids names events directly; subject_type + subject_ids names every stored event of the same source (for an app, the same project and environment) about those subjects. OpenVibe.Events rewrites each target into a tombstone (events.tombstone-payload@1) in the transaction that stores the directive. Naming another source's event is 403 events.redaction_not_allowed (the whole batch is refused); a malformed directive is 422 events.invalid_redaction. An event that carries a directive is never redacted itself.@@@ -6036,6 +6110,20 @@ export interface EventsSubscription {secret?: string;}++/** [email protected] (owner: events) */++/**++ * events.publish-request@1: the body of POST /api/v1/events on OpenVibe.Events (events.event.publish for services, events.app.publish for developer apps): one events.event-envelope@1, or a batch { events: [...] } of 1 to 100 envelopes stored all or nothing. trace_id, priority (important) and visibility (internal) default when left out. source must be the caller and event_type one of its prefixes (an app: app-<ULID> and app.<project_key>.*, actor the app or the user it acts for).++ */++export type EventsPublishRequest =++ | EventEnvelope++ | {++ /**++ * @minItems 1++ * @maxItems 100++ */++ events: [EventEnvelope, ...EventEnvelope[]];++ };++/** [email protected] (owner: chat) *//*** chat.message.deleted v1 (OpenVibe.Chat server/db/database.js _announceDeleted). Public messages were deleted: by a moderator, through Live's bridge, the author's or an anon's history, a time-range purge or the auto-delete sweep. Envelope: subject { type: chat_message, id: <first id> }, visibility public, priority important, actor service:chat. One event per 500 ids. Carries only ids, never the text, the author or who deleted it; payload.redacts makes OpenVibe.Events tombstone the chat.message.created of each id.@@@ -9789,102 +9877,1754 @@ export type SourcesSourceWriteRequest =string]| [-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ];++ /**++ * A date the source states.++ */++ published_at?: string | null;++ updated_at?: string | null;++ fields?: {++ [k: string]: (string | number | boolean | null) | undefined;++ };++ }++ | {++ reason: string;++ }++ | NoBody;++++/** [email protected] (owner: sources) */++/**++ * 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).++ */++export type SourcesItemReadResult =++ | {++ /**++ * @maxItems 500++ */++ items: SourceItem[];++ next_after: number;++ more?: boolean;++ sources: {++ [k: string]:++ | {++ status?: string;++ stale?: boolean;++ last_success_at?: string | null;++ }++ | undefined;++ };++ }++ | {++ item: SourceItem;++ source: {++ key: string;++ status?: string;++ stale?: boolean;++ last_success_at?: string | null;++ } | null;++ };++++/** [email protected] (owner: sources) */++/**++ * sources.source-read-result@1: answers of sources.source.read on OpenVibe.Sources. GET /api/v1/sources → { sources }; GET /api/v1/sources/:key → { source } (sources.source@1 with health); GET /api/v1/sources/:key/runs?before=&limit= → { runs, next_before } (newest first); GET /api/v1/runs?after=&source=&state=&limit= → { runs, next_after } (every source's fetch runs in order; state failed means any failure); GET /api/v1/health → { counts, attention } (sources by health status, and the failing, stale or never fetched ones).++ */++export type SourcesSourceReadResult =++ | {++ sources: Source[];++ }++ | {++ source: Source;++ }++ | {++ [k: string]: unknown | undefined;++ }++ | {++ counts: {++ [k: string]: number | undefined;++ };++ attention: {++ key: string;++ category?: string;++ type?: string;++ status: string;++ }[];++ };++++/** [email protected] (owner: search) */++/**++ * search.document.indexed v1 (OpenVibe.Search server/store.js announce). An applied index document (from an owner's *.index_document.upserted or the direct API) is servable: restricted (ACL only) or public. Sent on every applied revision that stays exposed, first time or not. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.++ */++export interface SearchDocumentIndexedPayload {++ owner: string;++ type: string;++ id: string;++ revision: number;++ exposure: "restricted" | "public_unlisted" | "public_listed";++ /**++ * true when Search already held an earlier revision of the document.++ */++ reindexed: boolean;++ /**++ * Only when the document is public (public_unlisted or public_listed); null otherwise or when it has none.++ */++ canonical_url: string | null;++}++++/** [email protected] (owner: search) */++/**++ * search.document.removed v1 (OpenVibe.Search server/store.js announce). A document's exposure went down: deleted, made draft or unpublished, its visibility changed, or it became noindex. Caches and sitemaps purge canonical_url. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.++ */++export interface SearchDocumentRemovedPayload {++ owner: string;++ type: string;++ id: string;++ revision: number;++ reason: "deleted" | "draft" | "not_published" | "visibility_changed" | "noindex";++ previous_exposure: "restricted" | "public_unlisted" | "public_listed";++ /**++ * Lower than previous_exposure. none: no longer served to anyone.++ */++ exposure: "none" | "restricted" | "public_unlisted";++ /**++ * The URL only when the document was public before (so it must be purged); null otherwise.++ */++ canonical_url: string | null;++}++++/** [email protected] (owner: search) */++/**++ * search.document-write-request@1: the request of search.document.write on OpenVibe.Search (a service writes and reads only the documents it owns). PUT /api/v1/documents/:owner/:type/:id: a search.index-document@1 (owner, type and id come from the path; a body that names others is 422). DELETE /api/v1/documents/:owner/:type/:id?revision= (a tombstone) and the owner reads (GET /api/v1/owners/:owner/documents, …/documents/:type/:id, …/rejections) take no body.++ */++export type SearchDocumentWriteRequest = IndexDocument | NoBody;++++/** [email protected] (owner: search) */++/**++ * search.owner-result@1: answers of search.document.write on OpenVibe.Search. PUT and DELETE /api/v1/documents/:owner/:type/:id → search.write-result@1 (stale and conflicting revisions are 409 problems); GET /api/v1/owners/:owner/documents?type=&after=&limit= → { owner, documents, next_after } (reconciliation: type, id, revision, deleted, exposure, hash, indexed_at); GET /api/v1/owners/:owner/documents/:type/:id → { document, effective_indexability, exposure, via, event_id, indexed_at } (the stored document); GET /api/v1/owners/:owner/rejections?after= → { rejections, next_after } (index-document events Search refused, at most 200).++ */++export type SearchOwnerResult =++ | SearchWriteResult++ | {++ owner: string;++ documents: {++ type: string;++ id: string;++ revision: number;++ deleted: boolean;++ exposure?: unknown;++ hash?: string | null;++ indexed_at?: string | null;++ }[];++ next_after: number | null;++ }++ | {++ document: IndexDocument;++ effective_indexability: unknown;++ exposure?: unknown;++ via?: unknown;++ event_id?: string | null;++ indexed_at?: string | null;++ }++ | {++ /**++ * @maxItems 200++ */++ rejections: {++ seq: number;++ event_id?: string | null;++ event_type?: string | null;++ type?: string | null;++ id?: string | null;++ revision?: number | null;++ code: string;++ detail?: string | null;++ at?: string | null;++ }[];++ next_after: number | null;++ };++++/** [email protected] (owner: search) */++/**++ * search.read-result@1: answers of search.query.run and search.query.delegate on OpenVibe.Search. GET /api/v1/search → search.query-result@1; GET /api/v1/suggest?q=&owner=&type=&limit= → { suggestions } (title prefixes, at most 20, only documents the caller may see); GET /api/v1/documents/:owner/:type/:id → { document } (one document by exact id, if the caller may see it; 404 otherwise).++ */++export type SearchReadResult =++ | SearchQueryResult++ | {++ /**++ * @maxItems 20++ */++ suggestions:++ | []++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }++ ]++ | [++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }]| [-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string,-- string++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ },++ {++ owner: string;++ type: string;++ id: string;++ title?: string | null;++ canonical_url?: string | null;++ }];-- /**-- * A date the source states.-- */-- published_at?: string | null;-- updated_at?: string | null;-- fields?: {-- [k: string]: (string | number | boolean | null) | undefined;-- };}| {-- reason: string;-- }-- | NoBody;----/** [email protected] (owner: search) */--/**-- * search.document.indexed v1 (OpenVibe.Search server/store.js announce). An applied index document (from an owner's *.index_document.upserted or the direct API) is servable: restricted (ACL only) or public. Sent on every applied revision that stays exposed, first time or not. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.-- */--export interface SearchDocumentIndexedPayload {-- owner: string;-- type: string;-- id: string;-- revision: number;-- exposure: "restricted" | "public_unlisted" | "public_listed";-- /**-- * true when Search already held an earlier revision of the document.-- */-- reindexed: boolean;-- /**-- * Only when the document is public (public_unlisted or public_listed); null otherwise or when it has none.-- */-- canonical_url: string | null;--}----/** [email protected] (owner: search) */--/**-- * search.document.removed v1 (OpenVibe.Search server/store.js announce). A document's exposure went down: deleted, made draft or unpublished, its visibility changed, or it became noindex. Caches and sitemaps purge canonical_url. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.-- */--export interface SearchDocumentRemovedPayload {-- owner: string;-- type: string;-- id: string;-- revision: number;-- reason: "deleted" | "draft" | "not_published" | "visibility_changed" | "noindex";-- previous_exposure: "restricted" | "public_unlisted" | "public_listed";-- /**-- * Lower than previous_exposure. none: no longer served to anyone.-- */-- exposure: "none" | "restricted" | "public_unlisted";-- /**-- * The URL only when the document was public before (so it must be purged); null otherwise.-- */-- canonical_url: string | null;--}++ document: {++ owner: string;++ type: string;++ id: string;++ revision: number;++ visibility?: string;++ title?: string | null;++ summary?: string | null;++ canonical_url?: string | null;++ facets?: unknown;++ language?: unknown;++ authorship?: unknown;++ provenance?: unknown;++ published_at?: unknown;++ updated_at?: unknown;++ indexable: boolean;++ };++ };/** [email protected] (owner: wiki) *//**@@@ -13039,6 +14779,112 @@ export type AiUsageResult =}[];};++/** [email protected] (owner: ai) */++/**++ * ai.run-citation@1: one source a run cited on OpenVibe.AI (the citations table): what the workflow was given or a citation the requester attached afterwards (provenance.attached), with trust and provenance.++ */++export interface AiRunCitation {++ id: number;++ run_id: string;++ ordinal?: number | null;++ source_type: string;++ source_id?: string | null;++ url?: string | null;++ title?: string | null;++ author?: string | null;++ published_at?: string | null;++ retrieved_at?: string | null;++ snippet?: string | null;++ content_hash?: string | null;++ trust?: {};++ provenance?: {};++ attached_by?: string | null;++ created_at?: string;++}++++/** [email protected] (owner: ai) */++/**++ * ai.run-create-request@1: bodies of ai.run.create on OpenVibe.AI. POST /api/v1/runs: ai.run-request@1. POST /api/v1/chat, /generate, /summarize, /classify, /extract, /enrich and /embed: a run of workflow ai.<op> whose input is the body itself (validated by that workflow's own input schema), except idempotency_key, target, attribution, on_behalf_of and options, which are the run's. POST /api/v1/runs/:id/citations: { citations } (1-50, each with a source_type; the requester only). POST /api/v1/runs/:id/cancel and /retry take no body.++ */++export type AiRunCreateRequest =++ | AiRunRequest++ | {++ /**++ * @minItems 1++ * @maxItems 50++ */++ citations: [++ {++ source_type: string;++ source_id?: string;++ url?: string;++ title?: string;++ author?: string;++ snippet?: string;++ provenance?: {};++ },++ ...{++ source_type: string;++ source_id?: string;++ url?: string;++ title?: string;++ author?: string;++ snippet?: string;++ provenance?: {};++ }[]++ ];++ }++ | {++ idempotency_key?: string;++ target?: unknown;++ attribution?: unknown;++ on_behalf_of?: unknown;++ options?: {};++ }++ | NoBody;++++/** [email protected] (owner: ai) */++/**++ * ai.run-create-result@1: answers of ai.run.create on OpenVibe.AI. POST /api/v1/runs and the direct operations → { run, replayed? } (201 when it finished or was served from cache within the wait, 202 with Location while queued or running, 200 with replayed true for an idempotent repeat); POST …/cancel → { run }; POST …/retry → { run } (a new run with retry_of); POST …/citations → 201 { citations }. A quota refusal or a full queue is 429 with Retry-After.++ */++export type AiRunCreateResult =++ | {++ run: AiRun;++ replayed?: true;++ }++ | {++ citations: AiRunCitation[];++ };++++/** [email protected] (owner: ai) */++/**++ * ai.run-read-result@1: answers of ai.run.read on OpenVibe.AI (the caller's own runs; usage readers may ask for all). GET /api/v1/runs?status=&workflow=&target=&limit=&before= → { runs }; GET /api/v1/runs/:id → { run, citations, requests } (the request log is metadata only: hashes, tokens, cost, latency, never prompts); GET /api/v1/runs/:id/citations → { citations }. Someone else's run is 404 run.not_found.++ */++export type AiRunReadResult =++ | {++ runs: AiRun[];++ }++ | {++ run: AiRun;++ citations: AiRunCitation[];++ requests: {++ id: number;++ seq: number;++ operation?: string | null;++ provider_key?: string | null;++ model_key?: string | null;++ status: string;++ fallback?: unknown;++ tokens_in?: number | null;++ tokens_out?: number | null;++ cost_usd?: number | null;++ latency_ms?: number | null;++ }[];++ }++ | {++ citations: AiRunCitation[];++ };++/** [email protected] (owner: tips) *//*** tips.interaction.ready v1 (OpenVibe.Tips server/domain/interactions.js settle, summary()). The interaction settled: it is active and its effects (overlay alert, chat line, paid message, TTS, media request) are queued. Envelope: subject { type: interaction, id: tint_… }, visibility internal, priority important, actor service:tips. Never emitted for tests or imports.@@@ -23551,6 +25397,16 @@ export interface NetworkStaffListResult {}[];}++/** [email protected] (owner: network) */++/**++ * network.module-write-request@1: the request of network.modules.write on OpenVibe.Network. PUT /internal/modules/:namespace/:subject: { data } — the record's new value, validated against the namespace's schema; If-Match names the revision read (412 if it moved). DELETE /internal/modules/:namespace/:subject (204) takes no body. The answer to PUT is modules.module-record@1.++ */++export type NetworkModuleWriteRequest =++ | {++ data: {};++ }++ | NoBody;++/** [email protected] (owner: tools) *//*** One tool on openvibe.tools as its registry describes it (GET /api/v1/tools/:id, and each item of GET /api/v1/tools; capability tools.tool.read; ADR-027). There is one descriptor per catalogue tool (GET /api/catalog.json tools[].id), built from the tool's own code, so its page, the run API, openvibe-sdk/tools, the OpenAPI document and the docs all read the same facts. EXECUTION is where the tool's engine runs for its page: client (in the browser; nothing leaves it), sync (a server request answered inline) or job (an asynchronous job on a satellite, tools.job@1). API says whether POST /api/v1/tools/{id}/run exposes the tool. A client tool has api true only when it also has a server engine (a pure transform that runs in Node), and its page stays browser-only. A page-only tool (yt) has api false and run null. A job tool's run creates a job of run.job.type whose input is { ...input, ...run.job.preset, tool: run.job.operation } (the preset and the operation always win). INPUT is the JSON Schema (2020-12) that a run request's input must match, embedded or as { $ref: https://openvibe.tools/api/v1/tools/{id}/schema#/$defs/input }. GET /api/v1/tools/:id/schema answers { $schema, $id, $defs: { input, output } }, and the list uses the $ref form. When api is false, $defs.input is false (the schema that accepts nothing), since there is no run to take an input. OUTPUT.SCHEMA describes result.data for every execution: inline and job tools alike. KEYWORDS (the catalogue's search terms) and EXAMPLES (sample runs for the docs and the OpenAPI document) are optional. NOT A TOOL: there is no planned status. A planned catalogue entry has no descriptor and is not listed, and neither is a mirror (a second build of another tool). GET /api/v1/tools/:id and /schema answer 404 tools.tool.not_found for either, with a detail that says the id is planned or names the tool it mirrors, as for an id that does not exist. RULES this schema enforces: api false has no run and no examples, and api true has a run and an input; an API job tool names its job, and only job tools name one; an API tool whose output is a file runs as a job (its results are served as job files); a tools.net.probe tool fetches (egress) and is never anonymous; an anonymous egress tool has a per-target throttle (limits.perTargetPerMinute); a client tool never fetches; an unavailable tool says why (statusReason); JSON output has a schema. contracts.tools.checkDescriptor(d) also checks what depends on the id (run.path, $ref targets), files.min <= files.max, and each example: its input against an embedded input schema and limits.maxInputBytes, and its files against files (count and accept).@@@ -24040,6 +25896,34 @@ export interface ToolsJobRequest {idempotency_key?: string;}++/** [email protected] (owner: tools) */++/**++ * tools.job-create-request@1: the request of tools.job.create on the Tools satellites that run jobs. POST /api/v1/jobs: tools.job-request@1 (JSON or multipart). POST /api/v1/jobs/:id/retry and PUT|DELETE /api/v1/jobs/:id/references/:ref take no body.++ */++export type ToolsJobCreateRequest = ToolsJobRequest | NoBody;++++/** [email protected] (owner: tools) */++/**++ * tools.job-read-result@1: answers of tools.job.read on the Tools satellites. GET /api/v1/jobs/:id → tools.job@1, and so is the data of every event on GET /api/v1/jobs/:id/events (Server-Sent Events, Last-Event-ID resume); GET /api/v1/jobs/:id/files/:n → the result file's bytes (Content-Type of the file; 409 tools.job.not_ready before the job succeeded).++ */++export type ToolsJobReadResult = ToolsJob | string;++++/** [email protected] (owner: tools) */++/**++ * tools.tool-read-result@1: answers of tools.tool.read on openvibe.tools (public registry; ADR-027). GET /api/v1/tools → tools.tool-list@1; GET /api/v1/tools/:id → tools.tool@1 with its schemas embedded; GET /api/v1/tools/:id/schema → the tool's own JSON Schema document { $schema, $id, $defs: { input, output } } (application/schema+json; input is false for a tool without an API input).++ */++export type ToolsToolReadResult =++ | ToolList++ | ToolDescriptor++ | {++ $schema: string;++ $id: string;++ $defs: {++ input: {} | boolean;++ output: {};++ };++ };++/** [email protected] (owner: news) *//*** news.source.ingested v1 (OpenVibe.News server/domain/ingest.js ingestedEvent, from apply and applyRemoval). An OpenVibe.Sources item of category news changed what News holds, in the transaction that applied it: created (a new news_source_items row, with its dedupe outcome and the cluster it joined; news.cluster.updated follows in the same transaction), updated (Sources sent a newer revision of an item News has; adds previous_revision) or removed (Sources removed the item: sticky, the stored summary is dropped; adds reason and origin). Items arrive through the Events webhook (sources.item.*) or the cursor pull; a replay, an older or equal revision, an item of another category, an update to a removed item and a removal of an item never ingested emit nothing, and an item without a title is news.source.failed (state rejected) instead. Deliberately left out: the headline, outlet, authors, published_at and licensed summary (GET /api/v1/source-items/:id with news.cluster.read) and the cluster_reason explanation. Envelope: subject { type: source_item, id: nsi_… }, visibility internal, priority low, actor service:news.@@@ -36623,6 +38507,30 @@ export interface MediaObjectVisibilityChangedPayload {changed_at: string;}++/** [email protected] (owner: media) */++/**++ * media.file-read-result@1: answers of media.object.read on OpenVibe.Media. GET /api/v1/:app/files/:key → media.file@1 (the file's metadata; 404 when it is not the tenant's); GET /f/:key → the bytes, with Content-Type and Range (a sandbox file only through its signed URL).++ */++export type MediaFileReadResult = MediaFile | string;++++/** [email protected] (owner: media) */++/**++ * media.file@1: one stored file of a tenant on OpenVibe.Media (server/files/routes.js filePublic): its key, owner, name, size, type and hash, and the URL it is served from (/f/<key>; a developer-project sandbox file gets a short-lived signed URL instead).++ */++export interface MediaFile {++ key: string;++ app_id: string;++ user_id?: string | number | null;++ original_name: string;++ size: number;++ mime: string;++ sha256?: string | null;++ url: string;++ sandbox?: true;++ url_expires_at?: string | number;++ created_at: string;++}++/** [email protected] (owner: community) *//*** community.paste.created v1 (OpenVibe.Community server/events.js, queued in the transaction of the change). A paste (text or screenshot) was created. Never the content or title. Envelope: subject { type: paste, id }, visibility public when the item is public else internal, priority low, actor the person or service:community.@@@ -37695,26 +39603,7 @@ export type CommunityPostWriteRequest =export type CommunityPulseWriteResult =| {created: boolean;-- item: {-- id: number;-- source: {-- service: string;-- type: string;-- id: string;-- };-- title: string;-- url: string;-- origin: "user" | "ai" | "system";-- label: "AI" | "System" | null;-- actor: {-- subject?: string | null;-- username?: string | null;-- display_name?: string | null;-- avatar_url?: string | null;-- profile_color?: string | null;-- } | null;-- occurred_at: string | null;-- };++ item: CommunityPulseItem;}| {removed: number;@@@ -38072,6 +39961,239 @@ export type CommunityPulseWriteRequest =}| NoBody;++/** [email protected] (owner: community) */++/**++ * community.comment-thread@1: one embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeThread): the entity it belongs to, its visibility and comment count. id is the sequential id for services and the access id (cth_…) for everyone else; a hidden thread shows moderators its count, others only that it is hidden.++ */++export interface CommunityCommentThread {++ id: number | string;++ access_id: string;++ ref: EntityRef;++ visibility: "public" | "hidden" | "locked";++ comment_count: number | null;++ created_at?: string | null;++ updated_at?: string | null;++}++++/** [email protected] (owner: community) */++/**++ * community.comment-write-request@1: bodies of community.comment.write on OpenVibe.Community (people, anonymous people with a name where the thread allows it, and services acting as X-OV-Subject or as AI). POST /api/v1/comments/threads/resolve: { ref } — get or create the thread of an entity (browsers only for the allow-listed entity types; a label is taken only from a service). POST /api/v1/comments/threads/:id/comments: { message, parent_id?, anon_name? } (message trimmed, at most 5000 characters; a reply to a reply joins the top-level comment). POST /api/v1/comments/:commentId/votes: { value: 1 | -1 | 0 } (people). GET /api/v1/comments/threads/:id and DELETE /api/v1/comments/:commentId take no body. Unknown fields are ignored.++ */++export type CommunityCommentWriteRequest =++ | {++ ref: EntityRef;++ }++ | {++ message: string;++ parent_id?: number | string | null;++ anon_name?: string | null;++ }++ | {++ value: 1 | -1 | 0 | "1" | "-1" | "0";++ }++ | NoBody;++++/** [email protected] (owner: community) */++/**++ * community.comment-write-result@1: answers of community.comment.write on OpenVibe.Community. POST …/threads/resolve → { thread, created } (201 when created); GET …/threads/:id?after=&sort=&limit=&parent= → { thread, comments, next_cursor, viewer }; POST …/threads/:id/comments → 201 { comment }; DELETE /api/v1/comments/:commentId → { ok, id }; POST …/:commentId/votes → { comment_id, score, upvotes, downvotes, my_vote }. Refusals are problem+json (thread.locked, auth.required, community.blocked, …).++ */++export type CommunityCommentWriteResult =++ | {++ thread: CommunityCommentThread;++ created: boolean;++ }++ | {++ thread: CommunityCommentThread;++ comments: CommunityComment[];++ next_cursor: string | null;++ viewer: {++ signed_in?: boolean;++ can_comment?: boolean;++ can_vote?: boolean;++ can_moderate?: boolean;++ };++ }++ | {++ comment: CommunityComment;++ }++ | {++ ok: true;++ id: number;++ }++ | {++ comment_id: number;++ score: number;++ upvotes: number;++ downvotes: number;++ my_vote: 1 | -1 | 0;++ };++++/** [email protected] (owner: community) */++/**++ * community.comment@1: one comment in an embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeComment), with its author (or anonymous name), score and the caller's vote, reply count and, for top-level comments in a page, the first replies. A deleted comment keeps its place with no author or message.++ */++export interface CommunityComment {++ id: number;++ thread_id: number | string;++ parent_id: number | null;++ origin: string;++ author: {++ subject?: string | null;++ username?: string | null;++ display_name?: string | null;++ avatar_url?: string | null;++ profile_color?: string | null;++ is_ai?: true;++ is_system?: true;++ } | null;++ anon_name?: string | null;++ display_name?: string | null;++ message: string | null;++ deleted: boolean;++ score: number;++ upvotes?: number;++ downvotes?: number;++ my_vote: 1 | -1 | 0;++ reply_count?: number | null;++ created_at?: string | null;++ updated_at?: string | null;++ edited_at?: string | null;++ can_edit: boolean;++ can_delete: boolean;++ replies?: {}[];++}++++/** [email protected] (owner: community) */++/**++ * community.forum-space@1: one forum space as OpenVibe.Community's /api/v1/spaces API shows it (server/forum/service.js shapeSpace): its slug, name, visibility, thread kind and statuses, style, whether votes and reactions are on, its group and parent board, the members-only gate and, in lists, its counts and last activity.++ */++export interface CommunityForumSpace {++ slug: string;++ name: string;++ description?: string | null;++ visibility: "public" | "members" | "staff";++ url: string;++ thread_kind: string;++ statuses: string[];++ style: "feed" | "forum";++ votes: boolean;++ reactions: boolean;++ group: {++ slug?: string;++ name?: string;++ } | null;++ parent: {++ slug?: string;++ name?: string;++ } | null;++ post_count?: number;++ thread_count?: number;++ last_activity_at?: string | null;++ members_only: {++ owner?: string;++ owner_username?: string | null;++ join_url?: string | null;++ } | null;++ last_post?: {} | null;++ children?: unknown[];++}++++/** [email protected] (owner: community) */++/**++ * community.pulse-item@1: one item of the network's public activity on OpenVibe.Community (server/pulse/service.js shape), with provenance: its source entity, title, url, origin (user, ai or system), the AI or System label, the actor (a person for user items, never for AI or system ones) and when it happened.++ */++export interface CommunityPulseItem {++ id: number;++ source: {++ service: string;++ type: string;++ id: string;++ };++ title: string;++ url: string;++ origin: "user" | "ai" | "system";++ label: "AI" | "System" | null;++ actor: {++ subject?: string | null;++ username?: string | null;++ display_name?: string | null;++ avatar_url?: string | null;++ profile_color?: string | null;++ is_ai?: true;++ is_system?: true;++ } | null;++ occurred_at: string | null;++}++++/** [email protected] (owner: community) */++/**++ * community.pulse-read-result@1: answers of community.pulse.read on OpenVibe.Community (anyone). GET /api/v1/pulse?origin=user|ai|system&after=&limit= → { items, next_cursor } (newest first, at most 100; an unknown origin or a cursor this API did not issue is 400). GET /pulse is the same activity as an HTML page.++ */++export type CommunityPulseReadResult =++ | {++ /**++ * @maxItems 100++ */++ items: CommunityPulseItem[];++ next_cursor: string | null;++ }++ | string;++++/** [email protected] (owner: community) */++/**++ * community.space-read-result@1: answers of community.space.read on OpenVibe.Community. GET /api/v1/spaces → { spaces, groups } (the spaces the caller can open, each with its last post, and the board index: groups in order with their top-level spaces and child boards); GET /api/v1/spaces/:space → { space } (a staff or members-only space the caller cannot open is 404 or 401/403 vip.members_only); GET /s is the board index as an HTML page.++ */++export type CommunitySpaceReadResult =++ | {++ spaces: CommunityForumSpace[];++ groups: {++ slug: string | null;++ name: string;++ description?: string | null;++ spaces: unknown[];++ }[];++ }++ | {++ space: CommunityForumSpace;++ }++ | string;++++/** [email protected] (owner: community) */++/**++ * community.thread-read-result@1: answers of community.thread.read on OpenVibe.Community. GET /api/v1/spaces/:space/threads?sort=&page=&limit=&category=&status= → a page of threads with the space, paging, categories, children and what the viewer may do; GET /api/v1/spaces/:space/threads/:slug?page= → the thread with a page of posts (reactions, author stats, crosspost info, attachments settings, paging, viewer). GET /s/:space and /s/:space/t/:slug are the same as HTML pages; GET /s/feed.xml and /s/:space/feed.xml are RSS feeds of public threads.++ */++export type CommunityThreadReadResult =++ | {++ space: {};++ sort?: string;++ page: number;++ per_page?: number;++ total: number;++ pages: number;++ categories?: unknown[];++ category?: string | null;++ status?: string | null;++ viewer?: {};++ children?: unknown[];++ groups?: unknown[];++ threads: CommunityForumThread[];++ }++ | {++ space: {};++ thread: CommunityForumThread;++ posts: CommunityForumPost[];++ reactions?: unknown[];++ crosspost?: {};++ categories?: unknown[];++ attachments?: {};++ page: number;++ per_page?: number;++ pages: number;++ total?: number;++ viewer?: {};++ }++ | string;++/** [email protected] (owner: openre) *//*** openre.destination@1: one restream destination of a stream on OpenRe.Stream (server/store/outputs.js publicDest). The stream key and SRT passphrase are write-only: only whether they are set and a hint of the key are shown.@@@ -5,8 +5,8 @@"status": "active","visibility": "first-party","description": "Create workflow runs (POST /api/v1/runs and the direct chat/generate/summarize/classify/extract/enrich/embed operations), cancel or retry the caller's own runs and attach citations to them. A token's `ns` claim limits which workflow namespaces it may run (live.*, wiki.*, ...). Quotas are checked before any provider call.",-- "inputSchema": "ai.run-request@1",-- "outputSchema": "ai.run@1",++ "inputSchema": "ai.run-create-request@1",++ "outputSchema": "ai.run-create-result@1","permissions": ["ai:run"],@@@ -5,7 +5,7 @@"status": "active","visibility": "first-party","description": "Read the caller's own runs, their citations and request-log metadata (hashes, tokens, latency; never raw prompts).",-- "outputSchema": "ai.run@1",++ "outputSchema": "ai.run-read-result@1","permissions": ["ai:read"],@@@ -5,7 +5,7 @@"status": "active","visibility": "public","description": "An app manages release metadata for ITSELF with its own token (audience openvibe.codes): create a release from a validated app or mod manifest, publish, deprecate (with a reason and an optional replacement) and revoke it. The token's sub must be app:<the app in the path>; people manage releases in the portal with their project role instead. Trust tiers are not part of this capability (staff metadata only).",-- "inputSchema": "codes.app-manifest@1",++ "inputSchema": "codes.release-manage-request@1","permissions": ["codes:releases:write"],"resourceConstraints": ["owner", "project"],"quotaClass": "codes-release-write",@@@ -5,8 +5,8 @@"status": "active","visibility": "first-party","description": "Typed comment threads as a service: resolve the thread of any EntityRef (browsers are limited to an allowlist of types), and comment, vote and delete one's own comments as the acting subject (X-OV-Subject), or comment as origin \"ai\" (never attributed to a person, roadmap 33). Ownership and limits are checked by Community.",-- "inputSchema": "common.entity-ref@1",-- "outputSchema": "common.entity-ref@1",++ "inputSchema": "community.comment-write-request@1",++ "outputSchema": "community.comment-write-result@1","permissions": ["community:write"],@@@ -5,7 +5,7 @@"status": "active","visibility": "public","description": "Read the network's public activity feed (Pulse): entity reference, title, URL and origin (user, ai or system), newest first with a cursor. Only public items are ever stored. Served to anyone (CORS allow-list for browsers). Live's /api/home/pulse is a different, Live-only feed.",-- "outputSchema": "common.entity-ref@1",++ "outputSchema": "community.pulse-read-result@1","permissions": ["community:read"],@@@ -5,7 +5,7 @@"status": "active","visibility": "public","description": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Community checks space visibility itself.",-- "outputSchema": "common.entity-ref@1",++ "outputSchema": "community.space-read-result@1","permissions": ["community:read"],@@@ -5,7 +5,7 @@"status": "active","visibility": "public","description": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds. Community checks space visibility and the VIP gate (403 vip.members_only). Creating threads and replies stays community.post.create.",-- "outputSchema": "common.entity-ref@1",++ "outputSchema": "community.thread-read-result@1","permissions": ["community:read"],@@@ -5,7 +5,7 @@"status": "active","visibility": "public","description": "Publish durable events (events.event-envelope@1) as a developer app (ADR-014). The app token (sub app:app_<ULID>, project_id prj_<ULID>, env) may publish only event types app.<project_key>.<name>[.<more>], where project_key is 'p' followed by the project's ULID in lowercase (prj_01JAB... -> p01jab...). source must be 'app-' followed by the app's ULID in lowercase; actor is the app (or the on_behalf_of user). Events stores the token's env with each event: sandbox events are never delivered to production subscriptions nor shown to production apps. Rate and retained bytes are limited per project and environment by OpenVibe.Events.",-- "inputSchema": "events.event-envelope@1",++ "inputSchema": "events.publish-request@1","permissions": ["events:write"],@@@ -5,7 +5,7 @@"status": "active","visibility": "internal","description": "Publish durable events (events.event-envelope@1) as the calling service: source must be the principal's service slug and event_type must use a prefix that source owns. Idempotent on event_id.",-- "inputSchema": "events.event-envelope@1",++ "inputSchema": "events.publish-request@1","permissions": ["events:write"],@@@ -5,8 +5,8 @@"status": "active","visibility": "first-party","description": "Resolve a subject (or a legacy service-local id) to its canonical SubjectRef and display projection.",-- "inputSchema": "identity.legacy-identity-map@1",-- "outputSchema": "identity.subject-ref@1",++ "inputSchema": "identity.resolve-request@1",++ "outputSchema": "identity.resolve-result@1","permissions": ["identity:read"],@@@ -5,7 +5,7 @@"status": "active","visibility": "public","description": "Read object metadata / bytes in a namespace.",-- "inputSchema": "media.media-ref@1",++ "inputSchema": "common.no-body@1","permissions": ["media:read"],@@@ -18,5 +18,5 @@"GET /api/v1/:app/files/:key","GET /f/:key"],-- "outputSchema": "common.binary@1"++ "outputSchema": "media.file-read-result@1"}@@@ -5,7 +5,7 @@"status": "active","visibility": "public","description": "Upload a file object into an app namespace.",-- "outputSchema": "media.media-ref@1",++ "outputSchema": "media.file@1","permissions": ["media:write"],@@@ -5,7 +5,7 @@"status": "active","visibility": "first-party","description": "Write a subject's record in a namespace the service owns (If-Match revision).",-- "inputSchema": "modules.module-record@1",++ "inputSchema": "network.module-write-request@1","outputSchema": "modules.module-record@1","permissions": ["modules:write"@@@ -5,7 +5,7 @@"status": "active","visibility": "first-party","description": "Index, replace or tombstone the calling service's own documents (search.index-document@1) and read its own inventory for reconciliation. The owner in the path must be the principal's service slug (svc:wiki writes owner wiki only). Revision-ordered: an older revision never overwrites a newer one, and at equal revision a deletion wins.",-- "inputSchema": "search.index-document@1",++ "inputSchema": "search.document-write-request@1","permissions": ["search:write"],@@@ -23,5 +23,5 @@"GET /api/v1/owners/:owner/documents/:type/:id","GET /api/v1/owners/:owner/rejections"],-- "outputSchema": "search.write-result@1"++ "outputSchema": "search.owner-result@1"}@@@ -19,5 +19,5 @@"GET /api/v1/documents/:owner/:type/:id"],"inputSchema": "search.query@1",-- "outputSchema": "search.query-result@1"++ "outputSchema": "search.read-result@1"}@@@ -19,5 +19,5 @@"GET /api/v1/documents/:owner/:type/:id"],"inputSchema": "search.query@1",-- "outputSchema": "search.query-result@1"++ "outputSchema": "search.read-result@1"}@@@ -5,7 +5,7 @@"status": "active","visibility": "first-party","description": "Read source items with provenance (source, canonical URL, retrieval time, content and raw-body hashes, parser version, terms) in change order, and one item with its revisions. Each page states the staleness of the sources it contains.",-- "outputSchema": "sources.item@1",++ "outputSchema": "sources.item-read-result@1","permissions": ["sources:read"],@@@ -5,7 +5,7 @@"status": "active","visibility": "first-party","description": "List registered sources with health and staleness, their fetch runs (every attempt with an explicit state) and the health summary. Credential values are never part of any response; only the variable name is.",-- "outputSchema": "sources.source@1",++ "outputSchema": "sources.source-read-result@1","permissions": ["sources:read"],@@@ -5,7 +5,7 @@"status": "active","visibility": "public","description": "Submit an asynchronous tool job (img.process, audio.process, docs.process) with its input files (tools.job-request@1, JSON or multipart); the job is owned by the calling principal. Idempotency-Key supported. Also the owner's other writes on a job: retry a failed job as a new one (idempotent: asking again returns that retry), and keep a succeeded job's result while a reference such as community:paste:p_123 points at it (PUT), or let it expire again (DELETE, after the last reference).",-- "inputSchema": "tools.job-request@1",++ "inputSchema": "tools.job-create-request@1","outputSchema": "tools.job@1","permissions": ["tools:jobs:write"@@@ -5,7 +5,7 @@"status": "active","visibility": "public","description": "Read a job the calling principal created: state, progress, result (Media object references or local files), SSE progress with Last-Event-ID resume, result files.",-- "outputSchema": "tools.job@1",++ "outputSchema": "tools.job-read-result@1","permissions": ["tools:jobs:read"],@@@ -5,7 +5,7 @@"status": "active","visibility": "public","description": "Read the tool registry (ADR-027): every tool's descriptor (tools.tool@1: execution, whether the run API exposes it, how to call it, input and output schemas, files, limits, auth, quota class, cost, egress, hosts, docs), filtered by family, execution, api, status or text. GET /api/v1/tools answers tools.tool-list@1 with schemas as { $ref }; GET /api/v1/tools/:id has them embedded; GET /api/v1/tools/:id/schema answers { $schema, $id, $defs: { input, output } }, with $defs.input false for a tool without an API. The gateway (https://openvibe.tools) answers for every tool, and each satellite for its own tools with its own live status. Open to everyone, anonymous callers included, from any origin (CORS *); cacheable (ETag, 304). A bad filter value is 400 tools.query.invalid; an unknown, planned or mirror id is 404 tools.tool.not_found, whose detail says which.",-- "outputSchema": "tools.tool@1",++ "outputSchema": "tools.tool-read-result@1","permissions": ["tools:catalog:read"],@@@ -229,12 +229,12 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscriptok(c && c.owner === 'tools' && c.inputSchema === 'tools.run-request@1' && c.outputSchema === 'tools.run@1' && c.implementedBy.some(r => r.startsWith('POST /api/v1/tools/:id/run')), `${id} is the run API`);}const [readCap, runCap, probeCap] = ['tools.tool.read', 'tools.tool.run', 'tools.net.probe'].map(id => capabilities.get(id));-- ok(readCap.visibility === 'public' && readCap.quotaClass === 'tools-read' && readCap.outputSchema === 'tools.tool@1' && ['GET /api/v1/tools', 'GET /api/v1/tools/:id', 'GET /api/v1/tools/:id/schema'].every(r => readCap.implementedBy.includes(r)), 'tools.tool.read: public registry routes');++ ok(readCap.visibility === 'public' && readCap.quotaClass === 'tools-read' && readCap.outputSchema === 'tools.tool-read-result@1' && JSON.stringify(contracts.schema('tools.tool-read-result').anyOf.slice(0, 2)) === JSON.stringify([{ $ref: 'tool-list.v1.json' }, { $ref: 'tool.v1.json' }]) && ['GET /api/v1/tools', 'GET /api/v1/tools/:id', 'GET /api/v1/tools/:id/schema'].every(r => readCap.implementedBy.includes(r)), 'tools.tool.read: public registry routes');ok(runCap.visibility === 'public' && runCap.quotaClass === 'tools-run', 'tools.tool.run is public');ok(probeCap.visibility === 'partner' && probeCap.quotaClass === 'tools-probe', 'tools.net.probe is partner: staff-set allowances only, never a default one');const jobCreate = capabilities.get('tools.job.create');-- ok(['POST /api/v1/jobs/:id/retry', 'PUT|DELETE /api/v1/jobs/:id/references/:ref'].every(r => jobCreate.implementedBy.includes(r)) && jobCreate.inputSchema === 'tools.job-request@1', 'tools.job.create covers submit, retry and references');-- ok(['tools.job.create', 'tools.job.read', 'tools.job.cancel'].every(id => capabilities.get(id).outputSchema === 'tools.job@1'), 'the job capabilities answer tools.job@1');++ ok(['POST /api/v1/jobs/:id/retry', 'PUT|DELETE /api/v1/jobs/:id/references/:ref'].every(r => jobCreate.implementedBy.includes(r)) && jobCreate.inputSchema === 'tools.job-create-request@1' && contracts.schema('tools.job-create-request').anyOf[0].$ref === 'job-request.v1.json', 'tools.job.create covers submit (tools.job-request@1), retry and references');++ ok(['tools.job.create', 'tools.job.cancel'].every(id => capabilities.get(id).outputSchema === 'tools.job@1') && capabilities.get('tools.job.read').outputSchema === 'tools.job-read-result@1' && contracts.schema('tools.job-read-result').anyOf[0].$ref === 'job.v1.json', 'the job capabilities answer tools.job@1 (reads also serve result files)');const svc = services.get('tools');ok(svc.ready === '/api/ready' && ['tools.tool.read', 'tools.tool.run', 'tools.net.probe'].every(c => svc.capabilities.includes(c)), 'the tools manifest lists the registry and run capabilities and its ready path');for (const t of ['created', 'started', 'succeeded', 'failed']) ok(contracts.resolve(`tools.job.${t}`).status === 'active', `tools.job.${t} is emitted (active)`);
@@ -12,6 +12,21 @@ Every active capability now names both schemas: **the gap list is empty (162 →
@@ -12,6 +12,21 @@ Every active capability now names both schemas: **the gap list is empty (162 →
Seven older capability schema references are corrected to what the routes take and return. `events.event.read` now answers `events.read-result@1` (was the bare envelope). `codes.release.read` answers the new **`codes.release-read-result@1`** (was `codes.app-manifest@1`). `community.paste.create` answers the new **`community.paste-create-result@1`**. `community.post.create` answers the new **`community.post-write-result@1`**, built on the new **`community.forum-thread@1`** and **`community.forum-post@1`** records (both were `common.entity-ref@1`). `community.pulse.write` takes the new **`community.pulse-write-request@1`** (was `common.entity-ref@1`). `games.mod.manage` takes the new **`games.mod-manage-request@1`**, and `games.mod.read` answers the new **`games.mod-read-result@1`** built on the new **`games.mod-view@1`** (both were `mods.mod-manifest@1`). No contract changes shape; only the capability manifests point elsewhere. Additive.
Seven older capability schema references are corrected to what the routes take and return. `events.event.read` now answers `events.read-result@1` (was the bare envelope). `codes.release.read` answers the new **`codes.release-read-result@1`** (was `codes.app-manifest@1`). `community.paste.create` answers the new **`community.paste-create-result@1`**. `community.post.create` answers the new **`community.post-write-result@1`**, built on the new **`community.forum-thread@1`** and **`community.forum-post@1`** records (both were `common.entity-ref@1`). `community.pulse.write` takes the new **`community.pulse-write-request@1`** (was `common.entity-ref@1`). `games.mod.manage` takes the new **`games.mod-manage-request@1`**, and `games.mod.read` answers the new **`games.mod-read-result@1`** built on the new **`games.mod-view@1`** (both were `mods.mod-manifest@1`). No contract changes shape; only the capability manifests point elsewhere. Additive.
+The remaining references that did not match their routes are corrected the same way (29 new schemas):
+- **codes.release.manage** takes **`codes.release-manage-request@1`** (was `codes.app-manifest@1`: the body wraps the manifest with kind, notes and publish; deprecate and revoke take a reason).
+- **community.pulse.read**, **community.space.read** and **community.thread.read** answer **`community.pulse-read-result@1`**, **`community.space-read-result@1`** and **`community.thread-read-result@1`**, with the new **`community.pulse-item@1`** and **`community.forum-space@1`** records. Each also covers its HTML pages and RSS feeds. All three were `common.entity-ref@1`.
+- **community.comment.write** takes **`community.comment-write-request@1`** and answers **`community.comment-write-result@1`**, with the new **`community.comment-thread@1`** and **`community.comment@1`** records. Both were `common.entity-ref@1`.
+- **sources.item.read** and **sources.source.read** answer **`sources.item-read-result@1`** and **`sources.source-read-result@1`**: one branch per route, lists and single records.
+- **ai.run.read** answers **`ai.run-read-result@1`**. **ai.run.create** takes **`ai.run-create-request@1`** (a run request, a direct operation's input, citations, or no body) and answers **`ai.run-create-result@1`** (`{ run }` or `{ citations }`, not the bare run), with the new **`ai.run-citation@1`**.
+- **events.event.publish** and **events.app.publish** take **`events.publish-request@1`**: one envelope, or `{ events: [...] }` of 1 to 100.
+- **identity.subject.resolve** takes **`identity.resolve-request@1`** and answers **`identity.resolve-result@1`** (**`identity.subject-projection@1`**, or `{ results }` for a batch). They were `identity.legacy-identity-map@1` and `identity.subject-ref@1`.
+- **network.modules.write** takes **`network.module-write-request@1`** (`{ data }`; the answer stays `modules.module-record@1`).
+- **media.object.read** takes no body and answers **`media.file-read-result@1`** (the file's metadata, or its bytes on `/f/:key`). **media.object.upload** answers the new **`media.file@1`** (Media answers a file record, not a `media.media-ref@1`).
+- **search.query.run** and **search.query.delegate** answer **`search.read-result@1`**: search, suggestions, or one document. **search.document.write** takes **`search.document-write-request@1`** and answers **`search.owner-result@1`**: write results, reconciliation pages, the stored document, rejections.
+- **tools.tool.read** answers **`tools.tool-read-result@1`** (the list, one tool, or its schema document). **tools.job.create** takes **`tools.job-create-request@1`**: retry and references take no body. **tools.job.read** answers **`tools.job-read-result@1`**: the job, or a result file's bytes. The Tools assertions in `test/run.js` name the new ids.
+
+No existing contract changes shape; only the manifests point elsewhere. Additive.
+
## 0.49.0 — 2026-09-25
## 0.49.0 — 2026-09-25
Platform blocks (roadmap WS-E task 5). **`network.block.changed`**: a person blocked or unblocked someone (`blocker`, `blocked`, `active`, a per-pair `revision`); Network keeps the blocks, keyed by subjects, and Chat and Community consume the event. New capability **`network.blocks.read`** (first-party, never for apps): `GET /internal/blocks?subject=` lists who a person blocked and who blocked them. Fixtures for a block and an unblock. Additive.
Platform blocks (roadmap WS-E task 5). **`network.block.changed`**: a person blocked or unblocked someone (`blocker`, `blocked`, `active`, a per-pair `revision`); Network keeps the blocks, keyed by subjects, and Chat and Community consume the event. New capability **`network.blocks.read`** (first-party, never for apps): `GET /internal/blocks?subject=` lists who a person blocked and who blocked them. Fixtures for a block and an unblock. Additive.
new file mode 100644
new file mode 100644
@@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/run-citation.v1.json",
+ "title": "AiRunCitation",
+ "description": "ai.run-citation@1: one source a run cited on OpenVibe.AI (the citations table): what the workflow was given or a citation the requester attached afterwards (provenance.attached), with trust and provenance.",
+ "type": "object",
+ "required": [
+ "id",
+ "run_id",
+ "source_type"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "run_id": {
+ "type": "string"
+ },
+ "ordinal": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "source_type": {
+ "type": "string"
+ },
+ "source_id": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "author": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "published_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "retrieved_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "snippet": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "content_hash": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "trust": {
+ "type": "object"
+ },
+ "provenance": {
+ "type": "object"
+ },
+ "attached_by": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "created_at": {
+ "type": "string"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/run-create-request.v1.json",
+ "title": "AiRunCreateRequest",
+ "description": "ai.run-create-request@1: bodies of ai.run.create on OpenVibe.AI. POST /api/v1/runs: ai.run-request@1. POST /api/v1/chat, /generate, /summarize, /classify, /extract, /enrich and /embed: a run of workflow ai.<op> whose input is the body itself (validated by that workflow's own input schema), except idempotency_key, target, attribution, on_behalf_of and options, which are the run's. POST /api/v1/runs/:id/citations: { citations } (1-50, each with a source_type; the requester only). POST /api/v1/runs/:id/cancel and /retry take no body.",
+ "anyOf": [
+ {
+ "$ref": "run-request.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "citations"
+ ],
+ "properties": {
+ "citations": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 50,
+ "items": {
+ "type": "object",
+ "required": [
+ "source_type"
+ ],
+ "properties": {
+ "source_type": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_.-]{1,63}$"
+ },
+ "source_id": {
+ "type": "string",
+ "maxLength": 4000
+ },
+ "url": {
+ "type": "string",
+ "maxLength": 4000
+ },
+ "title": {
+ "type": "string",
+ "maxLength": 4000
+ },
+ "author": {
+ "type": "string",
+ "maxLength": 4000
+ },
+ "snippet": {
+ "type": "string",
+ "maxLength": 4000
+ },
+ "provenance": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "description": "A direct operation: the workflow input fields plus idempotency_key, target, attribution, on_behalf_of and options.",
+ "properties": {
+ "idempotency_key": {
+ "type": "string"
+ },
+ "target": {},
+ "attribution": {},
+ "on_behalf_of": {},
+ "options": {
+ "type": "object"
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/run-create-result.v1.json",
+ "title": "AiRunCreateResult",
+ "description": "ai.run-create-result@1: answers of ai.run.create on OpenVibe.AI. POST /api/v1/runs and the direct operations → { run, replayed? } (201 when it finished or was served from cache within the wait, 202 with Location while queued or running, 200 with replayed true for an idempotent repeat); POST …/cancel → { run }; POST …/retry → { run } (a new run with retry_of); POST …/citations → 201 { citations }. A quota refusal or a full queue is 429 with Retry-After.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "run"
+ ],
+ "properties": {
+ "run": {
+ "$ref": "run.v1.json"
+ },
+ "replayed": {
+ "const": true
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "citations"
+ ],
+ "properties": {
+ "citations": {
+ "type": "array",
+ "items": {
+ "$ref": "run-citation.v1.json"
+ }
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/run-read-result.v1.json",
+ "title": "AiRunReadResult",
+ "description": "ai.run-read-result@1: answers of ai.run.read on OpenVibe.AI (the caller's own runs; usage readers may ask for all). GET /api/v1/runs?status=&workflow=&target=&limit=&before= → { runs }; GET /api/v1/runs/:id → { run, citations, requests } (the request log is metadata only: hashes, tokens, cost, latency, never prompts); GET /api/v1/runs/:id/citations → { citations }. Someone else's run is 404 run.not_found.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "runs"
+ ],
+ "properties": {
+ "runs": {
+ "type": "array",
+ "items": {
+ "$ref": "run.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "run",
+ "citations",
+ "requests"
+ ],
+ "properties": {
+ "run": {
+ "$ref": "run.v1.json"
+ },
+ "citations": {
+ "type": "array",
+ "items": {
+ "$ref": "run-citation.v1.json"
+ }
+ },
+ "requests": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "id",
+ "seq",
+ "status"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "seq": {
+ "type": "integer"
+ },
+ "operation": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "provider_key": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "model_key": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "status": {
+ "type": "string"
+ },
+ "fallback": {},
+ "tokens_in": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tokens_out": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "cost_usd": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "latency_ms": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "citations"
+ ],
+ "properties": {
+ "citations": {
+ "type": "array",
+ "items": {
+ "$ref": "run-citation.v1.json"
+ }
+ }
+ }
+ }
+ ]
+}
@@ -31,6 +31,33 @@
@@ -31,6 +31,33 @@
"status": "active",
"status": "active",
"adr": "ADR-003"
"adr": "ADR-003"
},
},
+ {
+ "id": "identity.resolve-request",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "identity/resolve-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "identity.resolve-result",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "identity/resolve-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "identity.subject-projection",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "identity/subject-projection.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "common.entity-ref",
"id": "common.entity-ref",
"version": "1.0.0",
"version": "1.0.0",
@@ -277,6 +304,15 @@
@@ -277,6 +304,15 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "codes.release-manage-request",
+ "version": "1.0.0",
+ "owner": "codes",
+ "schema": "codes/release-manage-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "events.redaction-directive",
"id": "events.redaction-directive",
"version": "1.0.0",
"version": "1.0.0",
@@ -369,6 +405,15 @@
@@ -369,6 +405,15 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "events.publish-request",
+ "version": "1.0.0",
+ "owner": "events",
+ "schema": "events/publish-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "chat.message.deleted",
"id": "chat.message.deleted",
"version": "1.0.0",
"version": "1.0.0",
@@ -457,6 +502,24 @@
@@ -457,6 +502,24 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "sources.item-read-result",
+ "version": "1.0.0",
+ "owner": "sources",
+ "schema": "sources/item-read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "sources.source-read-result",
+ "version": "1.0.0",
+ "owner": "sources",
+ "schema": "sources/source-read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "search.document.indexed",
"id": "search.document.indexed",
"version": "1.0.0",
"version": "1.0.0",
@@ -477,6 +540,33 @@
@@ -477,6 +540,33 @@
"status": "active",
"status": "active",
"adr": "ADR-018"
"adr": "ADR-018"
},
},
+ {
+ "id": "search.document-write-request",
+ "version": "1.0.0",
+ "owner": "search",
+ "schema": "search/document-write-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "search.owner-result",
+ "version": "1.0.0",
+ "owner": "search",
+ "schema": "search/owner-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "search.read-result",
+ "version": "1.0.0",
+ "owner": "search",
+ "schema": "search/read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "wiki.space.updated",
"id": "wiki.space.updated",
"version": "1.0.0",
"version": "1.0.0",
@@ -1149,6 +1239,42 @@
@@ -1149,6 +1239,42 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "ai.run-citation",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/run-citation.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.run-create-request",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/run-create-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.run-create-result",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/run-create-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.run-read-result",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/run-read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "tips.interaction.ready",
"id": "tips.interaction.ready",
"version": "1.0.0",
"version": "1.0.0",
@@ -2259,6 +2385,15 @@
@@ -2259,6 +2385,15 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "network.module-write-request",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "network/module-write-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "tools.tool",
"id": "tools.tool",
"version": "1.1.0",
"version": "1.1.0",
@@ -2319,6 +2454,33 @@
@@ -2319,6 +2454,33 @@
"status": "active",
"status": "active",
"adr": "ADR-027"
"adr": "ADR-027"
},
},
+ {
+ "id": "tools.job-create-request",
+ "version": "1.0.0",
+ "owner": "tools",
+ "schema": "tools/job-create-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "tools.job-read-result",
+ "version": "1.0.0",
+ "owner": "tools",
+ "schema": "tools/job-read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "tools.tool-read-result",
+ "version": "1.0.0",
+ "owner": "tools",
+ "schema": "tools/tool-read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "news.source.ingested",
"id": "news.source.ingested",
"version": "1.0.0",
"version": "1.0.0",
@@ -3687,6 +3849,24 @@
@@ -3687,6 +3849,24 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "media.file-read-result",
+ "version": "1.0.0",
+ "owner": "media",
+ "schema": "media/file-read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "media.file",
+ "version": "1.0.0",
+ "owner": "media",
+ "schema": "media/file.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "community.paste.created",
"id": "community.paste.created",
"version": "1.0.0",
"version": "1.0.0",
@@ -3978,6 +4158,87 @@
@@ -3978,6 +4158,87 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "community.comment-thread",
+ "version": "1.0.0",
+ "owner": "community",
+ "schema": "community/comment-thread.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "community.comment-write-request",
+ "version": "1.0.0",
+ "owner": "community",
+ "schema": "community/comment-write-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "community.comment-write-result",
+ "version": "1.0.0",
+ "owner": "community",
+ "schema": "community/comment-write-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "community.comment",
+ "version": "1.0.0",
+ "owner": "community",
+ "schema": "community/comment.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "community.forum-space",
+ "version": "1.0.0",
+ "owner": "community",
+ "schema": "community/forum-space.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "community.pulse-item",
+ "version": "1.0.0",
+ "owner": "community",
+ "schema": "community/pulse-item.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "community.pulse-read-result",
+ "version": "1.0.0",
+ "owner": "community",
+ "schema": "community/pulse-read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "community.space-read-result",
+ "version": "1.0.0",
+ "owner": "community",
+ "schema": "community/space-read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "community.thread-read-result",
+ "version": "1.0.0",
+ "owner": "community",
+ "schema": "community/thread-read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "openre.destination",
"id": "openre.destination",
"version": "1.0.0",
"version": "1.0.0",
new file mode 100644
new file mode 100644
@@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/codes/release-manage-request.v1.json",
+ "title": "CodesReleaseManageRequest",
+ "description": "codes.release-manage-request@1: bodies of codes.release.manage on OpenVibe.Codes (an app token managing its own releases). POST /api/v1/apps/:app/releases: { kind?, manifest, notes?, publish? } — kind mod or app (anything else is app); manifest must validate as codes.app-manifest@1 for an app or mods.mod-manifest@1 for a mod (422 manifest.invalid with the validation otherwise; a version that already has a release is 409); notes are cut to 2000 characters; publish true publishes the draft at once. POST /api/v1/releases/:id/deprecate: { reason, replacement? } (a reason is required, cut to 500 characters; replacement must be a published release of the same app). POST /api/v1/releases/:id/revoke: { reason } (required). POST /api/v1/releases/:id/publish takes no body. Unknown fields are ignored.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "manifest"
+ ],
+ "properties": {
+ "kind": {
+ "type": "string",
+ "description": "mod, or app (the default)."
+ },
+ "manifest": {
+ "anyOf": [
+ {
+ "$ref": "app-manifest.v1.json"
+ },
+ {
+ "$ref": "../mods/mod-manifest.v1.json"
+ }
+ ]
+ },
+ "notes": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "publish": {
+ "type": "boolean"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "reason"
+ ],
+ "properties": {
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "pattern": "\\S"
+ },
+ "replacement": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^(rel_[0-9A-HJKMNP-TV-Z]{26})?$"
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/comment-thread.v1.json",
+ "title": "CommunityCommentThread",
+ "description": "community.comment-thread@1: one embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeThread): the entity it belongs to, its visibility and comment count. id is the sequential id for services and the access id (cth_…) for everyone else; a hidden thread shows moderators its count, others only that it is hidden.",
+ "type": "object",
+ "required": [
+ "id",
+ "access_id",
+ "ref",
+ "visibility",
+ "comment_count"
+ ],
+ "properties": {
+ "id": {
+ "type": [
+ "integer",
+ "string"
+ ]
+ },
+ "access_id": {
+ "type": "string",
+ "pattern": "^cth_"
+ },
+ "ref": {
+ "$ref": "../common/entity-ref.v1.json"
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "hidden",
+ "locked"
+ ]
+ },
+ "comment_count": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/comment-write-request.v1.json",
+ "title": "CommunityCommentWriteRequest",
+ "description": "community.comment-write-request@1: bodies of community.comment.write on OpenVibe.Community (people, anonymous people with a name where the thread allows it, and services acting as X-OV-Subject or as AI). POST /api/v1/comments/threads/resolve: { ref } — get or create the thread of an entity (browsers only for the allow-listed entity types; a label is taken only from a service). POST /api/v1/comments/threads/:id/comments: { message, parent_id?, anon_name? } (message trimmed, at most 5000 characters; a reply to a reply joins the top-level comment). POST /api/v1/comments/:commentId/votes: { value: 1 | -1 | 0 } (people). GET /api/v1/comments/threads/:id and DELETE /api/v1/comments/:commentId take no body. Unknown fields are ignored.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "ref"
+ ],
+ "properties": {
+ "ref": {
+ "$ref": "../common/entity-ref.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "message"
+ ],
+ "properties": {
+ "message": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 5000,
+ "pattern": "\\S"
+ },
+ "parent_id": {
+ "type": [
+ "integer",
+ "string",
+ "null"
+ ]
+ },
+ "anon_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "value"
+ ],
+ "properties": {
+ "value": {
+ "enum": [
+ 1,
+ -1,
+ 0,
+ "1",
+ "-1",
+ "0"
+ ]
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,125 @@
@@ -0,0 +1,125 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/comment-write-result.v1.json",
+ "title": "CommunityCommentWriteResult",
+ "description": "community.comment-write-result@1: answers of community.comment.write on OpenVibe.Community. POST …/threads/resolve → { thread, created } (201 when created); GET …/threads/:id?after=&sort=&limit=&parent= → { thread, comments, next_cursor, viewer }; POST …/threads/:id/comments → 201 { comment }; DELETE /api/v1/comments/:commentId → { ok, id }; POST …/:commentId/votes → { comment_id, score, upvotes, downvotes, my_vote }. Refusals are problem+json (thread.locked, auth.required, community.blocked, …).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "thread",
+ "created"
+ ],
+ "properties": {
+ "thread": {
+ "$ref": "comment-thread.v1.json"
+ },
+ "created": {
+ "type": "boolean"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "thread",
+ "comments",
+ "next_cursor",
+ "viewer"
+ ],
+ "properties": {
+ "thread": {
+ "$ref": "comment-thread.v1.json"
+ },
+ "comments": {
+ "type": "array",
+ "items": {
+ "$ref": "comment.v1.json"
+ }
+ },
+ "next_cursor": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "viewer": {
+ "type": "object",
+ "properties": {
+ "signed_in": {
+ "type": "boolean"
+ },
+ "can_comment": {
+ "type": "boolean"
+ },
+ "can_vote": {
+ "type": "boolean"
+ },
+ "can_moderate": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "comment"
+ ],
+ "properties": {
+ "comment": {
+ "$ref": "comment.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "ok",
+ "id"
+ ],
+ "properties": {
+ "ok": {
+ "const": true
+ },
+ "id": {
+ "type": "integer"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "comment_id",
+ "score",
+ "upvotes",
+ "downvotes",
+ "my_vote"
+ ],
+ "properties": {
+ "comment_id": {
+ "type": "integer"
+ },
+ "score": {
+ "type": "integer"
+ },
+ "upvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "downvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "my_vote": {
+ "enum": [
+ 1,
+ -1,
+ 0
+ ]
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,163 @@
@@ -0,0 +1,163 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/comment.v1.json",
+ "title": "CommunityComment",
+ "description": "community.comment@1: one comment in an embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeComment), with its author (or anonymous name), score and the caller's vote, reply count and, for top-level comments in a page, the first replies. A deleted comment keeps its place with no author or message.",
+ "type": "object",
+ "required": [
+ "id",
+ "thread_id",
+ "parent_id",
+ "origin",
+ "author",
+ "message",
+ "deleted",
+ "score",
+ "my_vote",
+ "can_edit",
+ "can_delete"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "thread_id": {
+ "type": [
+ "integer",
+ "string"
+ ]
+ },
+ "parent_id": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "origin": {
+ "type": "string"
+ },
+ "author": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "subject": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "username": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "display_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "avatar_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "profile_color": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "is_ai": {
+ "const": true
+ },
+ "is_system": {
+ "const": true
+ }
+ }
+ },
+ "anon_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "display_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "message": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 5000
+ },
+ "deleted": {
+ "type": "boolean"
+ },
+ "score": {
+ "type": "integer"
+ },
+ "upvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "downvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "my_vote": {
+ "enum": [
+ 1,
+ -1,
+ 0
+ ]
+ },
+ "reply_count": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "edited_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "can_edit": {
+ "type": "boolean"
+ },
+ "can_delete": {
+ "type": "boolean"
+ },
+ "replies": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,139 @@
@@ -0,0 +1,139 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/forum-space.v1.json",
+ "title": "CommunityForumSpace",
+ "description": "community.forum-space@1: one forum space as OpenVibe.Community's /api/v1/spaces API shows it (server/forum/service.js shapeSpace): its slug, name, visibility, thread kind and statuses, style, whether votes and reactions are on, its group and parent board, the members-only gate and, in lists, its counts and last activity.",
+ "type": "object",
+ "required": [
+ "slug",
+ "name",
+ "visibility",
+ "url",
+ "thread_kind",
+ "statuses",
+ "style",
+ "votes",
+ "reactions",
+ "group",
+ "parent",
+ "members_only"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "members",
+ "staff"
+ ]
+ },
+ "url": {
+ "type": "string"
+ },
+ "thread_kind": {
+ "type": "string"
+ },
+ "statuses": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "style": {
+ "enum": [
+ "feed",
+ "forum"
+ ]
+ },
+ "votes": {
+ "type": "boolean"
+ },
+ "reactions": {
+ "type": "boolean"
+ },
+ "group": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ },
+ "parent": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ },
+ "post_count": {
+ "type": "integer"
+ },
+ "thread_count": {
+ "type": "integer"
+ },
+ "last_activity_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "members_only": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "owner_username": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "join_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ },
+ "last_post": {
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "children": {
+ "type": "array"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,113 @@
@@ -0,0 +1,113 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/pulse-item.v1.json",
+ "title": "CommunityPulseItem",
+ "description": "community.pulse-item@1: one item of the network's public activity on OpenVibe.Community (server/pulse/service.js shape), with provenance: its source entity, title, url, origin (user, ai or system), the AI or System label, the actor (a person for user items, never for AI or system ones) and when it happened.",
+ "type": "object",
+ "required": [
+ "id",
+ "source",
+ "title",
+ "url",
+ "origin",
+ "label",
+ "actor",
+ "occurred_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "source": {
+ "type": "object",
+ "required": [
+ "service",
+ "type",
+ "id"
+ ],
+ "properties": {
+ "service": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ }
+ }
+ },
+ "title": {
+ "type": "string",
+ "maxLength": 300
+ },
+ "url": {
+ "type": "string"
+ },
+ "origin": {
+ "enum": [
+ "user",
+ "ai",
+ "system"
+ ]
+ },
+ "label": {
+ "enum": [
+ "AI",
+ "System",
+ null
+ ]
+ },
+ "actor": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "subject": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "username": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "display_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "avatar_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "profile_color": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "is_ai": {
+ "const": true
+ },
+ "is_system": {
+ "const": true
+ }
+ }
+ },
+ "occurred_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/pulse-read-result.v1.json",
+ "title": "CommunityPulseReadResult",
+ "description": "community.pulse-read-result@1: answers of community.pulse.read on OpenVibe.Community (anyone). GET /api/v1/pulse?origin=user|ai|system&after=&limit= → { items, next_cursor } (newest first, at most 100; an unknown origin or a cursor this API did not issue is 400). GET /pulse is the same activity as an HTML page.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "items",
+ "next_cursor"
+ ],
+ "properties": {
+ "items": {
+ "type": "array",
+ "maxItems": 100,
+ "items": {
+ "$ref": "pulse-item.v1.json"
+ }
+ },
+ "next_cursor": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ },
+ {
+ "type": "string",
+ "contentMediaType": "text/html",
+ "description": "GET /pulse: the HTML page."
+ }
+ ]
+}
@@ -6,46 +6,31 @@
@@ -6,46 +6,31 @@
"anyOf": [
"anyOf": [
{
{
"type": "object",
"type": "object",
- "required": ["item", "created"],
+ "required": [
+ "item",
+ "created"
+ ],
"properties": {
"properties": {
- "created": { "type": "boolean" },
+ "created": {
+ "type": "boolean"
+ },
"item": {
"item": {
- "type": "object",
- "required": ["id", "source", "title", "url", "origin", "label", "actor", "occurred_at"],
- "properties": {
- "id": { "type": "integer" },
- "source": {
- "type": "object",
- "required": ["service", "type", "id"],
- "properties": {
- "service": { "type": "string" },
- "type": { "type": "string" },
- "id": { "type": "string" }
- }
- },
- "title": { "type": "string", "maxLength": 300 },
- "url": { "type": "string" },
- "origin": { "enum": ["user", "ai", "system"] },
- "label": { "enum": ["AI", "System", null] },
- "actor": {
- "type": ["object", "null"],
- "properties": {
- "subject": { "type": ["string", "null"] },
- "username": { "type": ["string", "null"] },
- "display_name": { "type": ["string", "null"] },
- "avatar_url": { "type": ["string", "null"] },
- "profile_color": { "type": ["string", "null"] }
- }
- },
- "occurred_at": { "type": ["string", "null"] }
- }
+ "$ref": "pulse-item.v1.json"
}
}
}
}
},
},
{
{
"type": "object",
"type": "object",
- "required": ["removed"],
- "properties": { "removed": { "type": "integer", "minimum": 0, "maximum": 1 } }
+ "required": [
+ "removed"
+ ],
+ "properties": {
+ "removed": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 1
+ }
+ }
}
}
]
]
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/space-read-result.v1.json",
+ "title": "CommunitySpaceReadResult",
+ "description": "community.space-read-result@1: answers of community.space.read on OpenVibe.Community. GET /api/v1/spaces → { spaces, groups } (the spaces the caller can open, each with its last post, and the board index: groups in order with their top-level spaces and child boards); GET /api/v1/spaces/:space → { space } (a staff or members-only space the caller cannot open is 404 or 401/403 vip.members_only); GET /s is the board index as an HTML page.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "spaces",
+ "groups"
+ ],
+ "properties": {
+ "spaces": {
+ "type": "array",
+ "items": {
+ "$ref": "forum-space.v1.json"
+ }
+ },
+ "groups": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "name",
+ "spaces"
+ ],
+ "properties": {
+ "slug": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "spaces": {
+ "type": "array"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "space"
+ ],
+ "properties": {
+ "space": {
+ "$ref": "forum-space.v1.json"
+ }
+ }
+ },
+ {
+ "type": "string",
+ "contentMediaType": "text/html",
+ "description": "GET /s: the board index page."
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,134 @@
@@ -0,0 +1,134 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/thread-read-result.v1.json",
+ "title": "CommunityThreadReadResult",
+ "description": "community.thread-read-result@1: answers of community.thread.read on OpenVibe.Community. GET /api/v1/spaces/:space/threads?sort=&page=&limit=&category=&status= → a page of threads with the space, paging, categories, children and what the viewer may do; GET /api/v1/spaces/:space/threads/:slug?page= → the thread with a page of posts (reactions, author stats, crosspost info, attachments settings, paging, viewer). GET /s/:space and /s/:space/t/:slug are the same as HTML pages; GET /s/feed.xml and /s/:space/feed.xml are RSS feeds of public threads.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "space",
+ "threads",
+ "page",
+ "pages",
+ "total"
+ ],
+ "properties": {
+ "space": {
+ "type": "object"
+ },
+ "sort": {
+ "type": "string"
+ },
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "per_page": {
+ "type": "integer"
+ },
+ "total": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "pages": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "categories": {
+ "type": "array"
+ },
+ "category": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "status": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "viewer": {
+ "type": "object"
+ },
+ "children": {
+ "type": "array"
+ },
+ "groups": {
+ "type": "array"
+ },
+ "threads": {
+ "type": "array",
+ "items": {
+ "$ref": "forum-thread.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "space",
+ "thread",
+ "posts",
+ "page",
+ "pages"
+ ],
+ "properties": {
+ "space": {
+ "type": "object"
+ },
+ "thread": {
+ "$ref": "forum-thread.v1.json"
+ },
+ "posts": {
+ "type": "array",
+ "items": {
+ "$ref": "forum-post.v1.json"
+ }
+ },
+ "reactions": {
+ "type": "array"
+ },
+ "crosspost": {
+ "type": "object"
+ },
+ "categories": {
+ "type": "array"
+ },
+ "attachments": {
+ "type": "object"
+ },
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "per_page": {
+ "type": "integer"
+ },
+ "pages": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "total": {
+ "type": "integer"
+ },
+ "viewer": {
+ "type": "object"
+ }
+ }
+ },
+ {
+ "type": "string",
+ "contentMediaType": "text/html",
+ "description": "GET /s/:space and /s/:space/t/:slug: HTML pages."
+ },
+ {
+ "type": "string",
+ "contentMediaType": "application/rss+xml",
+ "description": "GET /s/feed.xml and /s/:space/feed.xml: RSS feeds."
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/publish-request.v1.json",
+ "title": "EventsPublishRequest",
+ "description": "events.publish-request@1: the body of POST /api/v1/events on OpenVibe.Events (events.event.publish for services, events.app.publish for developer apps): one events.event-envelope@1, or a batch { events: [...] } of 1 to 100 envelopes stored all or nothing. trace_id, priority (important) and visibility (internal) default when left out. source must be the caller and event_type one of its prefixes (an app: app-<ULID> and app.<project_key>.*, actor the app or the user it acts for).",
+ "anyOf": [
+ {
+ "$ref": "event-envelope.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "events"
+ ],
+ "not": {
+ "required": [
+ "event_id"
+ ]
+ },
+ "properties": {
+ "events": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 100,
+ "items": {
+ "$ref": "event-envelope.v1.json"
+ }
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/identity/resolve-request.v1.json",
+ "title": "IdentityResolveRequest",
+ "description": "identity.resolve-request@1: the request of identity.subject.resolve on OpenVibe.Network. GET /internal/identity/resolve?subject_id= or ?system=&id=[&type=user] takes no body. POST /internal/identity/resolve-batch: { subject_ids } or { system, type?, ids } (at most 500; 413 beyond).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "subject_ids"
+ ],
+ "properties": {
+ "subject_ids": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 500,
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "system",
+ "ids"
+ ],
+ "properties": {
+ "system": {
+ "type": "string",
+ "minLength": 1
+ },
+ "type": {
+ "type": "string",
+ "default": "user"
+ },
+ "ids": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 500,
+ "items": {
+ "type": [
+ "string",
+ "integer"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/identity/resolve-result.v1.json",
+ "title": "IdentityResolveResult",
+ "description": "identity.resolve-result@1: answers of identity.subject.resolve on OpenVibe.Network. GET /internal/identity/resolve → one identity.subject-projection@1 (404 identity.subject_not_found when nothing matches); POST /internal/identity/resolve-batch → { results } keyed by each id asked, null for an id that resolves to nothing.",
+ "anyOf": [
+ {
+ "$ref": "subject-projection.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "results"
+ ],
+ "properties": {
+ "results": {
+ "type": "object",
+ "additionalProperties": {
+ "anyOf": [
+ {
+ "$ref": "subject-projection.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/identity/subject-projection.v1.json",
+ "title": "IdentitySubjectProjection",
+ "description": "identity.subject-projection@1: what OpenVibe.Network's identity resolver answers about a subject (server/identity/subjects.js resolve): the SubjectRef, the Network account behind it, its display fields, whether it is banned, and every legacy id mapped to it.",
+ "type": "object",
+ "required": [
+ "subject",
+ "username",
+ "display_name",
+ "avatar_url",
+ "banned"
+ ],
+ "properties": {
+ "subject": {
+ "$ref": "subject-ref.v1.json"
+ },
+ "network_user_id": {
+ "type": "integer"
+ },
+ "network_anon_id": {
+ "type": "integer"
+ },
+ "username": {
+ "type": "string"
+ },
+ "display_name": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "banned": {
+ "type": "boolean"
+ },
+ "legacy_ids": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "source_system",
+ "source_type",
+ "source_id"
+ ],
+ "properties": {
+ "source_system": {
+ "type": "string"
+ },
+ "source_type": {
+ "type": "string"
+ },
+ "source_id": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/media/file-read-result.v1.json",
+ "title": "MediaFileReadResult",
+ "description": "media.file-read-result@1: answers of media.object.read on OpenVibe.Media. GET /api/v1/:app/files/:key → media.file@1 (the file's metadata; 404 when it is not the tenant's); GET /f/:key → the bytes, with Content-Type and Range (a sandbox file only through its signed URL).",
+ "anyOf": [
+ {
+ "$ref": "file.v1.json"
+ },
+ {
+ "type": "string",
+ "contentEncoding": "binary",
+ "description": "GET /f/:key: the file."
+ }
+ ]
+}
@@ -2,11 +2,17 @@
@@ -2,11 +2,17 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/media/file-upload.v1.json",
"$id": "https://openvibe.network/contracts/media/file-upload.v1.json",
"title": "MediaFileUpload",
"title": "MediaFileUpload",
- "description": "media.file-upload@1: the multipart/form-data body of POST /api/v1/:app/files (media.object.upload, namespace :app): one part named `file` with the bytes and its filename and type. The tenant's quota is checked against the bytes; the answer is media.media-ref@1.",
+ "description": "media.file-upload@1: the multipart/form-data body of POST /api/v1/:app/files (media.object.upload, namespace :app): one part named `file` with the bytes and its filename and type. The tenant's quota is checked against the bytes; the answer is media.file@1 (201).",
"type": "object",
"type": "object",
- "required": ["file"],
+ "required": [
+ "file"
+ ],
"additionalProperties": true,
"additionalProperties": true,
"properties": {
"properties": {
- "file": { "type": "string", "contentEncoding": "binary", "description": "The multipart part named file (filename and Content-Type from the part)." }
+ "file": {
+ "type": "string",
+ "contentEncoding": "binary",
+ "description": "The multipart part named file (filename and Content-Type from the part)."
+ }
}
}
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/media/file.v1.json",
+ "title": "MediaFile",
+ "description": "media.file@1: one stored file of a tenant on OpenVibe.Media (server/files/routes.js filePublic): its key, owner, name, size, type and hash, and the URL it is served from (/f/<key>; a developer-project sandbox file gets a short-lived signed URL instead).",
+ "type": "object",
+ "required": [
+ "key",
+ "app_id",
+ "original_name",
+ "size",
+ "mime",
+ "url",
+ "created_at"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "app_id": {
+ "type": "string"
+ },
+ "user_id": {
+ "type": [
+ "string",
+ "integer",
+ "null"
+ ]
+ },
+ "original_name": {
+ "type": "string"
+ },
+ "size": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "mime": {
+ "type": "string"
+ },
+ "sha256": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "url": {
+ "type": "string"
+ },
+ "sandbox": {
+ "const": true
+ },
+ "url_expires_at": {
+ "type": [
+ "string",
+ "integer"
+ ]
+ },
+ "created_at": {
+ "type": "string"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/network/module-write-request.v1.json",
+ "title": "NetworkModuleWriteRequest",
+ "description": "network.module-write-request@1: the request of network.modules.write on OpenVibe.Network. PUT /internal/modules/:namespace/:subject: { data } — the record's new value, validated against the namespace's schema; If-Match names the revision read (412 if it moved). DELETE /internal/modules/:namespace/:subject (204) takes no body. The answer to PUT is modules.module-record@1.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "data"
+ ],
+ "properties": {
+ "data": {
+ "type": "object"
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/search/document-write-request.v1.json",
+ "title": "SearchDocumentWriteRequest",
+ "description": "search.document-write-request@1: the request of search.document.write on OpenVibe.Search (a service writes and reads only the documents it owns). PUT /api/v1/documents/:owner/:type/:id: a search.index-document@1 (owner, type and id come from the path; a body that names others is 422). DELETE /api/v1/documents/:owner/:type/:id?revision= (a tombstone) and the owner reads (GET /api/v1/owners/:owner/documents, …/documents/:type/:id, …/rejections) take no body.",
+ "anyOf": [
+ {
+ "$ref": "index-document.v1.json"
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,175 @@
@@ -0,0 +1,175 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/search/owner-result.v1.json",
+ "title": "SearchOwnerResult",
+ "description": "search.owner-result@1: answers of search.document.write on OpenVibe.Search. PUT and DELETE /api/v1/documents/:owner/:type/:id → search.write-result@1 (stale and conflicting revisions are 409 problems); GET /api/v1/owners/:owner/documents?type=&after=&limit= → { owner, documents, next_after } (reconciliation: type, id, revision, deleted, exposure, hash, indexed_at); GET /api/v1/owners/:owner/documents/:type/:id → { document, effective_indexability, exposure, via, event_id, indexed_at } (the stored document); GET /api/v1/owners/:owner/rejections?after= → { rejections, next_after } (index-document events Search refused, at most 200).",
+ "anyOf": [
+ {
+ "$ref": "write-result.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "owner",
+ "documents",
+ "next_after"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "documents": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "id",
+ "revision",
+ "deleted"
+ ],
+ "properties": {
+ "type": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ },
+ "revision": {
+ "type": "integer"
+ },
+ "deleted": {
+ "type": "boolean"
+ },
+ "exposure": {},
+ "hash": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "indexed_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "next_after": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "document",
+ "effective_indexability"
+ ],
+ "properties": {
+ "document": {
+ "$ref": "index-document.v1.json"
+ },
+ "effective_indexability": {},
+ "exposure": {},
+ "via": {},
+ "event_id": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "indexed_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "rejections",
+ "next_after"
+ ],
+ "properties": {
+ "rejections": {
+ "type": "array",
+ "maxItems": 200,
+ "items": {
+ "type": "object",
+ "required": [
+ "seq",
+ "code"
+ ],
+ "properties": {
+ "seq": {
+ "type": "integer"
+ },
+ "event_id": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "event_type": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "id": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "revision": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "code": {
+ "type": "string"
+ },
+ "detail": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "next_after": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/search/read-result.v1.json",
+ "title": "SearchReadResult",
+ "description": "search.read-result@1: answers of search.query.run and search.query.delegate on OpenVibe.Search. GET /api/v1/search → search.query-result@1; GET /api/v1/suggest?q=&owner=&type=&limit= → { suggestions } (title prefixes, at most 20, only documents the caller may see); GET /api/v1/documents/:owner/:type/:id → { document } (one document by exact id, if the caller may see it; 404 otherwise).",
+ "anyOf": [
+ {
+ "$ref": "query-result.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "suggestions"
+ ],
+ "properties": {
+ "suggestions": {
+ "type": "array",
+ "maxItems": 20,
+ "items": {
+ "type": "object",
+ "required": [
+ "owner",
+ "type",
+ "id"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "canonical_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "document"
+ ],
+ "properties": {
+ "document": {
+ "type": "object",
+ "required": [
+ "owner",
+ "type",
+ "id",
+ "revision",
+ "indexable"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ },
+ "revision": {
+ "type": "integer"
+ },
+ "visibility": {
+ "type": "string"
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "summary": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "canonical_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "facets": {},
+ "language": {},
+ "authorship": {},
+ "provenance": {},
+ "published_at": {},
+ "updated_at": {},
+ "indexable": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,96 @@
@@ -0,0 +1,96 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/sources/item-read-result.v1.json",
+ "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).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "items",
+ "next_after",
+ "sources"
+ ],
+ "properties": {
+ "items": {
+ "type": "array",
+ "maxItems": 500,
+ "items": {
+ "$ref": "item.v1.json"
+ }
+ },
+ "next_after": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "more": {
+ "type": "boolean"
+ },
+ "sources": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string"
+ },
+ "stale": {
+ "type": "boolean"
+ },
+ "last_success_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "item",
+ "source"
+ ],
+ "properties": {
+ "item": {
+ "$ref": "item.v1.json"
+ },
+ "source": {
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "key"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ },
+ "stale": {
+ "type": "boolean"
+ },
+ "last_success_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,192 @@
@@ -0,0 +1,192 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/sources/source-read-result.v1.json",
+ "title": "SourcesSourceReadResult",
+ "description": "sources.source-read-result@1: answers of sources.source.read on OpenVibe.Sources. GET /api/v1/sources → { sources }; GET /api/v1/sources/:key → { source } (sources.source@1 with health); GET /api/v1/sources/:key/runs?before=&limit= → { runs, next_before } (newest first); GET /api/v1/runs?after=&source=&state=&limit= → { runs, next_after } (every source's fetch runs in order; state failed means any failure); GET /api/v1/health → { counts, attention } (sources by health status, and the failing, stale or never fetched ones).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "sources"
+ ],
+ "properties": {
+ "sources": {
+ "type": "array",
+ "items": {
+ "$ref": "source.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "source"
+ ],
+ "properties": {
+ "source": {
+ "$ref": "source.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "runs"
+ ],
+ "anyOf": [
+ {
+ "required": [
+ "next_before"
+ ]
+ },
+ {
+ "required": [
+ "next_after"
+ ]
+ }
+ ],
+ "properties": {
+ "runs": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "source_key",
+ "state"
+ ],
+ "properties": {
+ "id": {
+ "type": [
+ "string",
+ "integer",
+ "null"
+ ]
+ },
+ "source_key": {
+ "type": "string"
+ },
+ "endpoint_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "trigger": {
+ "type": "string"
+ },
+ "started_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "finished_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "state": {
+ "type": "string"
+ },
+ "http_status": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "error_code": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "detail": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "bytes": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "raw_body_hash": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "parser_version": {},
+ "items": {
+ "type": "object"
+ },
+ "seq": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ }
+ }
+ },
+ "next_before": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "next_after": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "counts",
+ "attention"
+ ],
+ "properties": {
+ "counts": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "integer"
+ }
+ },
+ "attention": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "key",
+ "status"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "category": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/tools/job-create-request.v1.json",
+ "title": "ToolsJobCreateRequest",
+ "description": "tools.job-create-request@1: the request of tools.job.create on the Tools satellites that run jobs. POST /api/v1/jobs: tools.job-request@1 (JSON or multipart). POST /api/v1/jobs/:id/retry and PUT|DELETE /api/v1/jobs/:id/references/:ref take no body.",
+ "anyOf": [
+ {
+ "$ref": "job-request.v1.json"
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/tools/job-read-result.v1.json",
+ "title": "ToolsJobReadResult",
+ "description": "tools.job-read-result@1: answers of tools.job.read on the Tools satellites. GET /api/v1/jobs/:id → tools.job@1, and so is the data of every event on GET /api/v1/jobs/:id/events (Server-Sent Events, Last-Event-ID resume); GET /api/v1/jobs/:id/files/:n → the result file's bytes (Content-Type of the file; 409 tools.job.not_ready before the job succeeded).",
+ "anyOf": [
+ {
+ "$ref": "job.v1.json"
+ },
+ {
+ "type": "string",
+ "contentEncoding": "binary",
+ "description": "GET /api/v1/jobs/:id/files/:n."
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/tools/tool-read-result.v1.json",
+ "title": "ToolsToolReadResult",
+ "description": "tools.tool-read-result@1: answers of tools.tool.read on openvibe.tools (public registry; ADR-027). GET /api/v1/tools → tools.tool-list@1; GET /api/v1/tools/:id → tools.tool@1 with its schemas embedded; GET /api/v1/tools/:id/schema → the tool's own JSON Schema document { $schema, $id, $defs: { input, output } } (application/schema+json; input is false for a tool without an API input).",
+ "anyOf": [
+ {
+ "$ref": "tool-list.v1.json"
+ },
+ {
+ "$ref": "tool.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "$schema",
+ "$id",
+ "$defs"
+ ],
+ "properties": {
+ "$schema": {
+ "type": "string"
+ },
+ "$id": {
+ "type": "string"
+ },
+ "$defs": {
+ "type": "object",
+ "required": [
+ "input",
+ "output"
+ ],
+ "properties": {
+ "input": {
+ "type": [
+ "object",
+ "boolean"
+ ]
+ },
+ "output": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": 1, "run_id": "r" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": 55, "run_id": "run_01JAB2C3D4E5F6G7H8J9K0MNPS", "ordinal": 0, "source_type": "sources.item", "source_id": "itm_01JAB2C3D4E5F6G7H8J9K0MNPQ", "url": "https://news.example.com/a", "title": "A story", "author": null, "published_at": "2026-09-25", "retrieved_at": "2026-09-25T18:00:00Z", "snippet": null, "content_hash": null, "trust": {}, "provenance": {}, "attached_by": "workflow", "created_at": "2026-09-25T18:00:02.000Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+[ "not", "an", "object" ]
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "citations": [ { "source_type": "wiki.page", "url": "https://openvibe.wiki/openvibe/restreaming", "title": "Restreaming" } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "text": "Summarize this stream recap.", "options": { "max_words": 80 }, "idempotency_key": "recap-812" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "workflow": "network.site_copy", "input": { "sites": [] } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "replayed": true }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "run": { "id": "run_01JAB2C3D4E5F6G7H8J9K0MNPQ", "status": "succeeded", "workflow": { "key": "news.summarize_story", "version": 2 }, "template": { "key": "news.summarize_story", "version": 3 }, "route": { "key": "default.text", "version": 1 }, "requester": { "type": "service", "id": "news" }, "on_behalf_of": null, "attribution": null, "source_service": "news", "target": { "service": "news", "type": "story", "id": "sty_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "input": { "topic": "Transit strike", "sources": [{ "source_type": "news.item", "source_id": "itm_42" }] }, "output": { "headline": "Transit strike begins", "summary": "Workers walked out.", "citations": [0], "gaps": [] }, "error": null, "synthetic": false, "provenance": { "origin": "ai", "workflow": "news.summarize_story", "workflow_version": 2, "template_version": 3, "route": "default.text", "route_version": 1, "provider": "anthropic", "model": "claude-sonnet", "fallback_used": true, "run_id": "run_01JAB2C3D4E5F6G7H8J9K0MNPQ", "cached_from": null, "synthetic": false }, "usage": { "tokens_in": 1200, "tokens_out": 300, "cost_usd": 0.0081, "attempts": 2 }, "citations_count": 1, "retry_of": null, "idempotency_key": null, "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "created_at": "2026-09-23T10:00:00.000Z", "started_at": "2026-09-23T10:00:00.050Z", "finished_at": "2026-09-23T10:00:04.200Z"} }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "runs": [ { "id": "run_1" } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "runs": [ { "id": "run_01JAB2C3D4E5F6G7H8J9K0MNPQ", "status": "succeeded", "workflow": { "key": "news.summarize_story", "version": 2 }, "template": { "key": "news.summarize_story", "version": 3 }, "route": { "key": "default.text", "version": 1 }, "requester": { "type": "service", "id": "news" }, "on_behalf_of": null, "attribution": null, "source_service": "news", "target": { "service": "news", "type": "story", "id": "sty_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "input": { "topic": "Transit strike", "sources": [{ "source_type": "news.item", "source_id": "itm_42" }] }, "output": { "headline": "Transit strike begins", "summary": "Workers walked out.", "citations": [0], "gaps": [] }, "error": null, "synthetic": false, "provenance": { "origin": "ai", "workflow": "news.summarize_story", "workflow_version": 2, "template_version": 3, "route": "default.text", "route_version": 1, "provider": "anthropic", "model": "claude-sonnet", "fallback_used": true, "run_id": "run_01JAB2C3D4E5F6G7H8J9K0MNPQ", "cached_from": null, "synthetic": false }, "usage": { "tokens_in": 1200, "tokens_out": 300, "cost_usd": 0.0081, "attempts": 2 }, "citations_count": 1, "retry_of": null, "idempotency_key": null, "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "created_at": "2026-09-23T10:00:00.000Z", "started_at": "2026-09-23T10:00:00.050Z", "finished_at": "2026-09-23T10:00:04.200Z"} ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "run": { "id": "run_01JAB2C3D4E5F6G7H8J9K0MNPQ", "status": "succeeded", "workflow": { "key": "news.summarize_story", "version": 2 }, "template": { "key": "news.summarize_story", "version": 3 }, "route": { "key": "default.text", "version": 1 }, "requester": { "type": "service", "id": "news" }, "on_behalf_of": null, "attribution": null, "source_service": "news", "target": { "service": "news", "type": "story", "id": "sty_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "input": { "topic": "Transit strike", "sources": [{ "source_type": "news.item", "source_id": "itm_42" }] }, "output": { "headline": "Transit strike begins", "summary": "Workers walked out.", "citations": [0], "gaps": [] }, "error": null, "synthetic": false, "provenance": { "origin": "ai", "workflow": "news.summarize_story", "workflow_version": 2, "template_version": 3, "route": "default.text", "route_version": 1, "provider": "anthropic", "model": "claude-sonnet", "fallback_used": true, "run_id": "run_01JAB2C3D4E5F6G7H8J9K0MNPQ", "cached_from": null, "synthetic": false }, "usage": { "tokens_in": 1200, "tokens_out": 300, "cost_usd": 0.0081, "attempts": 2 }, "citations_count": 1, "retry_of": null, "idempotency_key": null, "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "created_at": "2026-09-23T10:00:00.000Z", "started_at": "2026-09-23T10:00:00.050Z", "finished_at": "2026-09-23T10:00:04.200Z"}, "citations": [ { "id": 55, "run_id": "run_01JAB2C3D4E5F6G7H8J9K0MNPS", "ordinal": 0, "source_type": "sources.item", "source_id": "itm_01JAB2C3D4E5F6G7H8J9K0MNPQ", "url": "https://news.example.com/a", "title": "A story", "author": null, "published_at": "2026-09-25", "retrieved_at": "2026-09-25T18:00:00Z", "snippet": null, "content_hash": null, "trust": {}, "provenance": {}, "attached_by": "workflow", "created_at": "2026-09-25T18:00:02.000Z" } ], "requests": [ { "id": 7, "seq": 1, "operation": "chat", "provider_key": "anthropic", "model_key": "claude-sonnet-4-5", "status": "succeeded", "fallback": 0, "tokens_in": 812, "tokens_out": 140, "cost_usd": 0.0045, "latency_ms": 2310 } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "reason": " ", "manifest": 5 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "kind": "app", "manifest": { "id": "app_01JABCDEFGHJKMNPQRSTVWXYZ0", "name": "My app", "version": "0.1.0", "publisher": { "type": "app", "id": "app_01JABCDEFGHJKMNPQRSTVWXYZ0" }, "project_id": "prj_01JABCDEFGHJKMNPQRSTVWXYZ0", "environment": "sandbox", "capabilities": [], "compatibility": { "contracts": "^0.26.0", "sdk": "^0.2.2" }}, "notes": "First release.", "publish": true }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "reason": "superseded by 1.5.0", "replacement": "rel_01JAB2C3D4E5F6G7H8J9K0MNPS" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "access_id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "ref": { "service": "live", "type": "vod", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "visibility": "archived", "comment_count": 3, "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "access_id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "ref": { "service": "live", "type": "vod", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "visibility": "public", "comment_count": 3, "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "message": " ", "value": 3, "ref": "live:vod:1" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "message": "That ending!", "parent_id": 810 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "ref": { "service": "live", "type": "vod", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "ok": false, "id": 1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "thread": { "id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "access_id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "ref": { "service": "live", "type": "vod", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "visibility": "public", "comment_count": 3, "created_at": "2026-09-20T10:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z" }, "comments": [ { "id": 812, "thread_id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "parent_id": null, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": null }, "anon_name": null, "display_name": "Alice", "message": "That ending!", "deleted": false, "score": 2, "upvotes": 2, "downvotes": 0, "my_vote": 0, "reply_count": 0, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "edited_at": null, "can_edit": false, "can_delete": false } ], "next_cursor": null, "viewer": { "signed_in": true, "can_comment": true, "can_vote": true, "can_moderate": false } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "comment_id": 812, "score": 3, "upvotes": 3, "downvotes": 0, "my_vote": 1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": 812, "thread_id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "parent_id": null, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": null }, "anon_name": null, "display_name": "Alice", "message": "That ending!", "deleted": false, "score": 2, "upvotes": 2, "downvotes": 0, "my_vote": 5, "reply_count": 0, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "edited_at": null, "can_edit": false, "can_delete": false }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": 812, "thread_id": "cth_Ab3dE5gH7jK9mN1pQ3sT5v", "parent_id": null, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": null }, "anon_name": null, "display_name": "Alice", "message": "That ending!", "deleted": false, "score": 2, "upvotes": 2, "downvotes": 0, "my_vote": 0, "reply_count": 0, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "edited_at": null, "can_edit": false, "can_delete": false }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "wiki", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": 9001, "source": { "service": "live", "type": "clip", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "title": "Alice clipped a clean 1v3", "url": "https://openvibe.live/clip/med_01JAB2C3D4E5F6G7H8J9K0MNPQ", "origin": "bot", "label": "AI", "actor": null, "occurred_at": "2026-09-25T18:00:00.000Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": 9001, "source": { "service": "live", "type": "clip", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "title": "Alice clipped a clean 1v3", "url": "https://openvibe.live/clip/med_01JAB2C3D4E5F6G7H8J9K0MNPQ", "origin": "ai", "label": "AI", "actor": null, "occurred_at": "2026-09-25T18:00:00.000Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "items": "none" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+"<!doctype html><title>Pulse</title>"
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "items": [ { "id": 9001, "source": { "service": "live", "type": "clip", "id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "title": "Alice clipped a clean 1v3", "url": "https://openvibe.live/clip/med_01JAB2C3D4E5F6G7H8J9K0MNPQ", "origin": "ai", "label": "AI", "actor": null, "occurred_at": "2026-09-25T18:00:00.000Z" } ], "next_cursor": "WyIyMDI2LTA5LTI1IDE4OjAwOjAwIiw5MDAxXQ" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "spaces": [ { "slug": "x" } ], "groups": [] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "spaces": [ { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" } ], "groups": [ { "slug": "make", "name": "Make things", "description": null, "spaces": [ { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" } ] } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "space": { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "space": {}, "threads": [], "page": 0, "pages": 1, "total": 0 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+"<?xml version=\"1.0\"?><rss version=\"2.0\"></rss>"
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "space": { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }, "thread": { "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/s/streaming/t/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 0 }, "posts": [ { "attachments": [], "pastes": [ { "slug": "k3JfQ2", "title": "ffmpeg restream flags", "language": "text", "type": "paste", "url": "/p/k3JfQ2", "screenshot_url": null, "lines": 1, "excerpt": "ffmpeg -re -i in.flv -c copy -f flv rtmp://example/live" } ], "id": 5120, "thread_id": 311, "is_opening": true, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "body_markdown": "What bitrates do you use?", "body_html": "<p>What bitrates do you use?</p>", "revision": 1, "deleted": false, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "can_edit": true } ], "reactions": [], "crosspost": { "from": null, "to": [], "targets": [] }, "categories": [], "attachments": { "enabled": true, "max": 4, "max_bytes": 8388608 }, "page": 1, "per_page": 20, "pages": 1, "total": 1, "viewer": { "signed_in": true } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "space": { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }, "sort": "hot", "page": 1, "per_page": 25, "total": 1, "pages": 1, "categories": [], "category": null, "status": null, "viewer": { "can_start": true, "can_moderate": false, "signed_in": true }, "children": [], "threads": [ { "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/s/streaming/t/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 0 } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "events": [] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "events": [ { "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "event_type": "media.vod.ready", "version": 1, "source": "media", "actor": { "type": "service", "id": "media" }, "timestamp": "2026-09-22T12:00:00Z", "priority": "important", "visibility": "internal", "subject": { "type": "vod", "id": "1203", "revision": 4 }, "payload": { "duration": 3600 }} ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "event_type": "media.vod.ready", "version": 1, "source": "media", "actor": { "type": "service", "id": "media" }, "timestamp": "2026-09-22T12:00:00Z", "priority": "important", "visibility": "internal", "subject": { "type": "vod", "id": "1203", "revision": 4 }, "payload": { "duration": 3600 }}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "ids": ["17"] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "system": "live", "type": "user", "ids": ["17", 42] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "subject_ids": [ "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ" ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "results": { "x": 5 } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "results": { "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ": { "subject": { "type": "user", "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "network_user_id": 1042, "username": "alice", "display_name": "Alice", "avatar_url": null, "banned": false, "legacy_ids": [ { "source_system": "live", "source_type": "user", "source_id": "17" } ] }, "usr_01JAB2C3D4E5F6G7H8J9K0MNPR": null } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "subject": { "type": "user", "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "username": "alice", "display_name": "Alice", "avatar_url": null, "banned": "no" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "subject": { "type": "user", "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ" }, "network_user_id": 1042, "username": "alice", "display_name": "Alice", "avatar_url": null, "banned": false, "legacy_ids": [ { "source_system": "live", "source_type": "user", "source_id": "17" } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "key": "k" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+"<bytes>"
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "key": "f_9c1b2e7d", "app_id": "live", "user_id": 17, "original_name": "overlay.png", "size": 48213, "mime": "image/png", "sha256": "3f1c0b8e2a9d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b", "url": "/f/f_9c1b2e7d", "created_at": "2026-09-25 18:00:00" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "key": "k", "app_id": "live", "original_name": "x", "size": -1, "mime": "image/png", "url": "/f/k", "created_at": "x" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "key": "f_9c1b2e7d", "app_id": "live", "user_id": 17, "original_name": "overlay.png", "size": 48213, "mime": "image/png", "sha256": "3f1c0b8e2a9d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b", "url": "/f/f_9c1b2e7d", "created_at": "2026-09-25 18:00:00" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "data": "on" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "data": { "send": true, "volume": 0.8 } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "owner": "Wiki!", "type": "page", "id": "x", "revision": -1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "owner": "wiki", "type": "page", "id": "pg_01J0000000000000000000000Z", "revision": 3, "visibility": "public", "canonical_url": "https://openvibe.wiki/w/example", "title": "Example page", "summary": "A short summary.", "body": "Plain text body.", "facets": { "space": "general", "tags": [ "example" ] }, "language": "en", "authorship": "human", "publication_state": "published", "published_at": "2026-09-22T12:00:00Z", "indexability": { "decision": "index", "reasons": [] }, "provenance": [ { "service": "sources", "type": "item", "id": "itm_01J0000000000000000000000Z" } ]}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "documents": [] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "outcome": "applied", "revision": 2, "stored_revision": 1, "deleted": false }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "owner": "wiki", "documents": [ { "type": "page", "id": "restreaming", "revision": 3, "deleted": false, "exposure": "public", "hash": "ab12", "indexed_at": "2026-09-24T10:00:01.000Z" } ], "next_after": null }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "rejections": [ { "seq": 4, "event_id": "evt_01JAB2C3D4E5F6G7H8J9K0MNPQ", "event_type": "wiki.index_document.upserted", "type": "page", "id": "x", "revision": 2, "code": "search.bad_document", "detail": "title is required", "at": "2026-09-25T18:00:00.000Z" } ], "next_after": 4 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "suggestions": "none" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "document": { "owner": "wiki", "type": "page", "id": "restreaming", "revision": 3, "visibility": "public", "title": "Restreaming", "summary": null, "canonical_url": "https://openvibe.wiki/openvibe/restreaming", "facets": {}, "language": "en", "authorship": "human", "provenance": [], "published_at": "2026-09-20T10:00:00Z", "updated_at": "2026-09-24T10:00:00Z", "indexable": true } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "results": [ { "owner": "live", "type": "vod", "id": "912", "revision": 3, "visibility": "public", "title": "Building a forum", "summary": null, "canonical_url": "https://openvibe.live/vod/912", "facets": { "channel": "alex" }, "language": null, "authorship": "human", "provenance": [], "published_at": "2026-09-24T19:00:00.000Z", "updated_at": null, "indexable": true, "snippet_html": "building a <mark>forum</mark>" } ], "next_cursor": null }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "suggestions": [ { "owner": "wiki", "type": "page", "id": "restreaming", "title": "Restreaming", "canonical_url": "https://openvibe.wiki/openvibe/restreaming" } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "items": [], "sources": {} }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "items": [ { "id": "itm_01J0000000000000000000000Z", "source_key": "nasa-news", "category": "news", "kind": "article", "identity": "https://www.nasa.gov/news-release/example/", "canonical_url": "https://www.nasa.gov/news-release/example/", "title": "Example release", "summary": null, "authors": [], "published_at": "2026-09-22T15:00:00Z", "source_updated_at": null, "fields": {}, "revision": 1, "provenance": { "retrieved_at": "2026-09-23T01:00:00Z", "first_seen_at": "2026-09-23T01:00:00Z", "content_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "raw_body_hash": null, "parser_version": "rss@1", "fetch_run_id": "frn_01J0000000000000000000000Z", "license_note": null, "terms_note": "US government work", "entered_by": null }, "removed": null} ], "next_after": 1204, "more": false, "sources": { "example-news": { "status": "healthy", "stale": false, "last_success_at": "2026-09-25T18:00:00.000Z" } } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "item": { "id": "itm_01J0000000000000000000000Z", "source_key": "nasa-news", "category": "news", "kind": "article", "identity": "https://www.nasa.gov/news-release/example/", "canonical_url": "https://www.nasa.gov/news-release/example/", "title": "Example release", "summary": null, "authors": [], "published_at": "2026-09-22T15:00:00Z", "source_updated_at": null, "fields": {}, "revision": 1, "provenance": { "retrieved_at": "2026-09-23T01:00:00Z", "first_seen_at": "2026-09-23T01:00:00Z", "content_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "raw_body_hash": null, "parser_version": "rss@1", "fetch_run_id": "frn_01J0000000000000000000000Z", "license_note": null, "terms_note": "US government work", "entered_by": null }, "removed": null}, "source": null }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "runs": [] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "counts": { "healthy": 11, "stale": 1 }, "attention": [ { "key": "example-deals", "category": "deals", "type": "api", "status": "stale" } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "sources": [ { "key": "nasa-news", "name": "NASA news releases", "type": "rss", "category": "news", "homepage_url": "https://www.nasa.gov/", "endpoints": [{ "url": "https://www.nasa.gov/news-release/feed/" }], "auth": { "mode": "none" }, "robots_note": "robots.txt allows the feed path (checked 2026-09-23)", "terms_note": "US government work; verify before enabling", "license_note": null, "min_interval_ms": 60000, "poll_interval_sec": 3600, "stale_after_sec": 172800, "max_items": 200, "enabled": false, "review_required": true, "sensitivity": "none", "default_indexability": "noindex"} ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "runs": [ { "id": "run_1", "source_key": "example-news", "endpoint_url": "https://news.example.com/feed.xml", "trigger": "schedule", "started_at": "2026-09-25T18:00:00.000Z", "finished_at": "2026-09-25T18:00:01.000Z", "state": "ok", "http_status": 200, "error_code": null, "detail": null, "bytes": 18234, "raw_body_hash": null, "parser_version": "rss@1", "items": { "seen": 20, "created": 2 }, "seq": 991 } ], "next_after": 991 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+"img.process"
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "type": "img.process", "input": { "tool": "convert", "format": "webp", "quality": 80 }}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": 5 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+"<bytes>"
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ", "object": "tools.job", "service": "img", "type": "img.process", "type_version": 1, "state": "queued", "progress": { "percent": null, "message": null }, "attempts": 0, "max_attempts": 3, "cancel_requested": false, "created_at": "2026-09-23T09:00:00.000Z", "started_at": null, "finished_at": null, "expires_at": "2026-09-23T10:00:00.000Z", "result": null, "error": null, "retryable": false, "retry_of": null, "retried_by": null, "references": [], "links": { "self": "/api/v1/jobs/job_01JAB2C3D4E5F6G7H8J9K0MNPQ", "events": "/api/v1/jobs/job_01JAB2C3D4E5F6G7H8J9K0MNPQ/events", "cancel": "/api/v1/jobs/job_01JAB2C3D4E5F6G7H8J9K0MNPQ", "retry": null, "retried_by": null }}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "$schema": 5, "$defs": {} }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "tools": [ { "id": "png", "family": "img", "name": "PNG Converter", "summary": "Convert JPG, WebP, AVIF, HEIC, GIF, BMP, TIFF and more to PNG.", "keywords": [ "jpg to png", "webp to png", "heic to png", "convert image to png" ], "status": "stable", "execution": "job", "api": true, "run": { "method": "POST", "path": "/api/v1/tools/png/run", "job": { "type": "img.process", "operation": "convert", "preset": { "format": "png" } }, "legacy": [ "POST /api/process", "POST /api/process/direct" ] }, "input": { "$ref": "https://openvibe.tools/api/v1/tools/png/schema#/$defs/input" }, "files": { "min": 1, "max": 1, "accept": [ "image/*" ], "maxBytes": 52428800 }, "output": { "kind": "file", "mime": [ "image/png" ] }, "limits": { "timeoutMs": 120000, "maxPixels": 100000000 }, "auth": { "anonymous": true, "capability": "tools.tool.run" }, "quotaClass": "tools-job", "cost": 5, "egress": false, "hosts": [ "png.openvibe.tools" ], "docs": "https://openvibe.tools/tool/png" }, { "id": "dns", "family": "net", "name": "DNS Lookup", "summary": "Look up A, AAAA, MX, TXT, NS, CNAME and other records for a domain.", "keywords": [ "dns lookup", "mx record lookup", "txt record check", "nslookup online" ], "status": "stable", "execution": "sync", "api": true, "run": { "method": "POST", "path": "/api/v1/tools/dns/run", "job": null, "legacy": [ "GET /api/net/dns/:target" ] }, "input": { "$ref": "https://openvibe.tools/api/v1/tools/dns/schema#/$defs/input" }, "files": null, "output": { "kind": "json", "schema": { "$ref": "https://openvibe.tools/api/v1/tools/dns/schema#/$defs/output" } }, "limits": { "timeoutMs": 10000, "maxInputBytes": 1024, "perTargetPerMinute": 30 }, "auth": { "anonymous": true, "capability": "tools.tool.run" }, "quotaClass": "tools-fetch", "cost": 1, "egress": true, "hosts": [ "dns.openvibe.tools" ], "docs": "https://openvibe.tools/tool/dns" }, { "id": "port", "family": "net", "name": "Port Checker", "summary": "Check whether TCP ports on a host are open.", "keywords": [ "open port checker", "port scanner" ], "status": "beta", "execution": "sync", "api": true, "run": { "method": "POST", "path": "/api/v1/tools/port/run", "job": null, "legacy": [ "GET /api/net/port/:target" ] }, "input": { "$ref": "https://openvibe.tools/api/v1/tools/port/schema#/$defs/input" }, "files": null, "output": { "kind": "json", "schema": { "$ref": "https://openvibe.tools/api/v1/tools/port/schema#/$defs/output" } }, "limits": { "timeoutMs": 15000, "perTargetPerMinute": 6 }, "auth": { "anonymous": false, "capability": "tools.net.probe" }, "quotaClass": "tools-probe", "cost": 5, "egress": true, "hosts": [ "port.openvibe.tools" ], "docs": "https://openvibe.tools/tool/port" }, { "id": "yt", "family": "media", "name": "YouTube Downloader", "summary": "Save a YouTube video or its audio.", "status": "stable", "execution": "job", "api": false, "run": null, "input": null, "files": null, "output": { "kind": "file", "mime": [ "video/mp4", "audio/mpeg" ] }, "limits": { "timeoutMs": 900000, "maxDurationSec": 3600, "perTargetPerMinute": 20 }, "auth": { "anonymous": true, "capability": "tools.tool.run" }, "quotaClass": "tools-download", "cost": 50, "egress": true, "hosts": [ "youtube-downloader.openvibe.tools", "yt.openvibe.tools" ], "docs": "https://openvibe.tools/tool/yt" } ], "count": 4, "updated_at": "2026-09-23T21:00:00.000Z", "families": [ { "id": "img", "name": "Image Tools", "count": 1 }, { "id": "net", "name": "Network Tools", "count": 2 }, { "id": "media", "name": "Media", "count": 1 } ]}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "dns", "family": "net", "name": "DNS Lookup", "summary": "Look up A, AAAA, MX, TXT, NS, CNAME and other records for a domain.", "keywords": [ "dns lookup", "mx record lookup", "txt record check", "nslookup online" ], "status": "stable", "execution": "sync", "api": true, "run": { "method": "POST", "path": "/api/v1/tools/dns/run", "job": null, "legacy": [ "GET /api/net/dns/:target" ] }, "input": { "$ref": "https://openvibe.tools/api/v1/tools/dns/schema#/$defs/input" }, "files": null, "examples": [ { "title": "MX records of a domain", "input": { "target": "example.com", "type": "MX" } } ], "output": { "kind": "json", "schema": { "$ref": "https://openvibe.tools/api/v1/tools/dns/schema#/$defs/output" } }, "limits": { "timeoutMs": 10000, "maxInputBytes": 1024, "perTargetPerMinute": 30 }, "auth": { "anonymous": true, "capability": "tools.tool.run" }, "quotaClass": "tools-fetch", "cost": 1, "egress": true, "hosts": [ "dns.openvibe.tools" ], "docs": "https://openvibe.tools/tool/dns"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://openvibe.tools/api/v1/tools/dns/schema", "$defs": { "input": { "type": "object" }, "output": { "type": "object" } } }
@@ -272,6 +272,170 @@
@@ -272,6 +272,170 @@
]
]
}
}
},
},
+ "identity.resolve-request": {
+ "version": "1.0.0",
+ "owner": "network",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/identity/resolve-request.v1.json",
+ "title": "IdentityResolveRequest",
+ "description": "identity.resolve-request@1: the request of identity.subject.resolve on OpenVibe.Network. GET /internal/identity/resolve?subject_id= or ?system=&id=[&type=user] takes no body. POST /internal/identity/resolve-batch: { subject_ids } or { system, type?, ids } (at most 500; 413 beyond).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "subject_ids"
+ ],
+ "properties": {
+ "subject_ids": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 500,
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "system",
+ "ids"
+ ],
+ "properties": {
+ "system": {
+ "type": "string",
+ "minLength": 1
+ },
+ "type": {
+ "type": "string",
+ "default": "user"
+ },
+ "ids": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 500,
+ "items": {
+ "type": [
+ "string",
+ "integer"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "identity.resolve-result": {
+ "version": "1.0.0",
+ "owner": "network",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/identity/resolve-result.v1.json",
+ "title": "IdentityResolveResult",
+ "description": "identity.resolve-result@1: answers of identity.subject.resolve on OpenVibe.Network. GET /internal/identity/resolve → one identity.subject-projection@1 (404 identity.subject_not_found when nothing matches); POST /internal/identity/resolve-batch → { results } keyed by each id asked, null for an id that resolves to nothing.",
+ "anyOf": [
+ {
+ "$ref": "subject-projection.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "results"
+ ],
+ "properties": {
+ "results": {
+ "type": "object",
+ "additionalProperties": {
+ "anyOf": [
+ {
+ "$ref": "subject-projection.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ "identity.subject-projection": {
+ "version": "1.0.0",
+ "owner": "network",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/identity/subject-projection.v1.json",
+ "title": "IdentitySubjectProjection",
+ "description": "identity.subject-projection@1: what OpenVibe.Network's identity resolver answers about a subject (server/identity/subjects.js resolve): the SubjectRef, the Network account behind it, its display fields, whether it is banned, and every legacy id mapped to it.",
+ "type": "object",
+ "required": [
+ "subject",
+ "username",
+ "display_name",
+ "avatar_url",
+ "banned"
+ ],
+ "properties": {
+ "subject": {
+ "$ref": "subject-ref.v1.json"
+ },
+ "network_user_id": {
+ "type": "integer"
+ },
+ "network_anon_id": {
+ "type": "integer"
+ },
+ "username": {
+ "type": "string"
+ },
+ "display_name": {
+ "type": "string"
+ },
+ "avatar_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "banned": {
+ "type": "boolean"
+ },
+ "legacy_ids": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "source_system",
+ "source_type",
+ "source_id"
+ ],
+ "properties": {
+ "source_system": {
+ "type": "string"
+ },
+ "source_type": {
+ "type": "string"
+ },
+ "source_id": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"common.entity-ref": {
"common.entity-ref": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "contracts",
"owner": "contracts",
@@ -1618,7 +1782,7 @@
@@ -1618,7 +1782,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/media/file-upload.v1.json",
"$id": "https://openvibe.network/contracts/media/file-upload.v1.json",
"title": "MediaFileUpload",
"title": "MediaFileUpload",
- "description": "media.file-upload@1: the multipart/form-data body of POST /api/v1/:app/files (media.object.upload, namespace :app): one part named `file` with the bytes and its filename and type. The tenant's quota is checked against the bytes; the answer is media.media-ref@1.",
+ "description": "media.file-upload@1: the multipart/form-data body of POST /api/v1/:app/files (media.object.upload, namespace :app): one part named `file` with the bytes and its filename and type. The tenant's quota is checked against the bytes; the answer is media.file@1 (201).",
"type": "object",
"type": "object",
"required": [
"required": [
"file"
"file"
@@ -3576,6 +3740,73 @@
@@ -3576,6 +3740,73 @@
]
]
}
}
},
},
+ "codes.release-manage-request": {
+ "version": "1.0.0",
+ "owner": "codes",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/codes/release-manage-request.v1.json",
+ "title": "CodesReleaseManageRequest",
+ "description": "codes.release-manage-request@1: bodies of codes.release.manage on OpenVibe.Codes (an app token managing its own releases). POST /api/v1/apps/:app/releases: { kind?, manifest, notes?, publish? } — kind mod or app (anything else is app); manifest must validate as codes.app-manifest@1 for an app or mods.mod-manifest@1 for a mod (422 manifest.invalid with the validation otherwise; a version that already has a release is 409); notes are cut to 2000 characters; publish true publishes the draft at once. POST /api/v1/releases/:id/deprecate: { reason, replacement? } (a reason is required, cut to 500 characters; replacement must be a published release of the same app). POST /api/v1/releases/:id/revoke: { reason } (required). POST /api/v1/releases/:id/publish takes no body. Unknown fields are ignored.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "manifest"
+ ],
+ "properties": {
+ "kind": {
+ "type": "string",
+ "description": "mod, or app (the default)."
+ },
+ "manifest": {
+ "anyOf": [
+ {
+ "$ref": "app-manifest.v1.json"
+ },
+ {
+ "$ref": "../mods/mod-manifest.v1.json"
+ }
+ ]
+ },
+ "notes": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "publish": {
+ "type": "boolean"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "reason"
+ ],
+ "properties": {
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "pattern": "\\S"
+ },
+ "replacement": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^(rel_[0-9A-HJKMNP-TV-Z]{26})?$"
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
"events.redaction-directive": {
"events.redaction-directive": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "events",
"owner": "events",
@@ -4235,6 +4466,43 @@
@@ -4235,6 +4466,43 @@
}
}
}
}
},
},
+ "events.publish-request": {
+ "version": "1.0.0",
+ "owner": "events",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/publish-request.v1.json",
+ "title": "EventsPublishRequest",
+ "description": "events.publish-request@1: the body of POST /api/v1/events on OpenVibe.Events (events.event.publish for services, events.app.publish for developer apps): one events.event-envelope@1, or a batch { events: [...] } of 1 to 100 envelopes stored all or nothing. trace_id, priority (important) and visibility (internal) default when left out. source must be the caller and event_type one of its prefixes (an app: app-<ULID> and app.<project_key>.*, actor the app or the user it acts for).",
+ "anyOf": [
+ {
+ "$ref": "event-envelope.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "events"
+ ],
+ "not": {
+ "required": [
+ "event_id"
+ ]
+ },
+ "properties": {
+ "events": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 100,
+ "items": {
+ "$ref": "event-envelope.v1.json"
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
"chat.message.deleted": {
"chat.message.deleted": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "chat",
"owner": "chat",
@@ -5070,6 +5338,304 @@
@@ -5070,6 +5338,304 @@
]
]
}
}
},
},
+ "sources.item-read-result": {
+ "version": "1.0.0",
+ "owner": "sources",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/sources/item-read-result.v1.json",
+ "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).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "items",
+ "next_after",
+ "sources"
+ ],
+ "properties": {
+ "items": {
+ "type": "array",
+ "maxItems": 500,
+ "items": {
+ "$ref": "item.v1.json"
+ }
+ },
+ "next_after": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "more": {
+ "type": "boolean"
+ },
+ "sources": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string"
+ },
+ "stale": {
+ "type": "boolean"
+ },
+ "last_success_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "item",
+ "source"
+ ],
+ "properties": {
+ "item": {
+ "$ref": "item.v1.json"
+ },
+ "source": {
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "key"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ },
+ "stale": {
+ "type": "boolean"
+ },
+ "last_success_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ "sources.source-read-result": {
+ "version": "1.0.0",
+ "owner": "sources",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/sources/source-read-result.v1.json",
+ "title": "SourcesSourceReadResult",
+ "description": "sources.source-read-result@1: answers of sources.source.read on OpenVibe.Sources. GET /api/v1/sources → { sources }; GET /api/v1/sources/:key → { source } (sources.source@1 with health); GET /api/v1/sources/:key/runs?before=&limit= → { runs, next_before } (newest first); GET /api/v1/runs?after=&source=&state=&limit= → { runs, next_after } (every source's fetch runs in order; state failed means any failure); GET /api/v1/health → { counts, attention } (sources by health status, and the failing, stale or never fetched ones).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "sources"
+ ],
+ "properties": {
+ "sources": {
+ "type": "array",
+ "items": {
+ "$ref": "source.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "source"
+ ],
+ "properties": {
+ "source": {
+ "$ref": "source.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "runs"
+ ],
+ "anyOf": [
+ {
+ "required": [
+ "next_before"
+ ]
+ },
+ {
+ "required": [
+ "next_after"
+ ]
+ }
+ ],
+ "properties": {
+ "runs": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "source_key",
+ "state"
+ ],
+ "properties": {
+ "id": {
+ "type": [
+ "string",
+ "integer",
+ "null"
+ ]
+ },
+ "source_key": {
+ "type": "string"
+ },
+ "endpoint_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "trigger": {
+ "type": "string"
+ },
+ "started_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "finished_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "state": {
+ "type": "string"
+ },
+ "http_status": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "error_code": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "detail": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "bytes": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "raw_body_hash": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "parser_version": {},
+ "items": {
+ "type": "object"
+ },
+ "seq": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ }
+ }
+ },
+ "next_before": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "next_after": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "counts",
+ "attention"
+ ],
+ "properties": {
+ "counts": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "integer"
+ }
+ },
+ "attention": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "key",
+ "status"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "category": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
"search.document.indexed": {
"search.document.indexed": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "search",
"owner": "search",
@@ -5202,6 +5768,326 @@
@@ -5202,6 +5768,326 @@
}
}
}
}
},
},
+ "search.document-write-request": {
+ "version": "1.0.0",
+ "owner": "search",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/search/document-write-request.v1.json",
+ "title": "SearchDocumentWriteRequest",
+ "description": "search.document-write-request@1: the request of search.document.write on OpenVibe.Search (a service writes and reads only the documents it owns). PUT /api/v1/documents/:owner/:type/:id: a search.index-document@1 (owner, type and id come from the path; a body that names others is 422). DELETE /api/v1/documents/:owner/:type/:id?revision= (a tombstone) and the owner reads (GET /api/v1/owners/:owner/documents, …/documents/:type/:id, …/rejections) take no body.",
+ "anyOf": [
+ {
+ "$ref": "index-document.v1.json"
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "search.owner-result": {
+ "version": "1.0.0",
+ "owner": "search",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/search/owner-result.v1.json",
+ "title": "SearchOwnerResult",
+ "description": "search.owner-result@1: answers of search.document.write on OpenVibe.Search. PUT and DELETE /api/v1/documents/:owner/:type/:id → search.write-result@1 (stale and conflicting revisions are 409 problems); GET /api/v1/owners/:owner/documents?type=&after=&limit= → { owner, documents, next_after } (reconciliation: type, id, revision, deleted, exposure, hash, indexed_at); GET /api/v1/owners/:owner/documents/:type/:id → { document, effective_indexability, exposure, via, event_id, indexed_at } (the stored document); GET /api/v1/owners/:owner/rejections?after= → { rejections, next_after } (index-document events Search refused, at most 200).",
+ "anyOf": [
+ {
+ "$ref": "write-result.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "owner",
+ "documents",
+ "next_after"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "documents": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "id",
+ "revision",
+ "deleted"
+ ],
+ "properties": {
+ "type": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ },
+ "revision": {
+ "type": "integer"
+ },
+ "deleted": {
+ "type": "boolean"
+ },
+ "exposure": {},
+ "hash": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "indexed_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "next_after": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "document",
+ "effective_indexability"
+ ],
+ "properties": {
+ "document": {
+ "$ref": "index-document.v1.json"
+ },
+ "effective_indexability": {},
+ "exposure": {},
+ "via": {},
+ "event_id": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "indexed_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "rejections",
+ "next_after"
+ ],
+ "properties": {
+ "rejections": {
+ "type": "array",
+ "maxItems": 200,
+ "items": {
+ "type": "object",
+ "required": [
+ "seq",
+ "code"
+ ],
+ "properties": {
+ "seq": {
+ "type": "integer"
+ },
+ "event_id": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "event_type": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "id": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "revision": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "code": {
+ "type": "string"
+ },
+ "detail": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "next_after": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ "search.read-result": {
+ "version": "1.0.0",
+ "owner": "search",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/search/read-result.v1.json",
+ "title": "SearchReadResult",
+ "description": "search.read-result@1: answers of search.query.run and search.query.delegate on OpenVibe.Search. GET /api/v1/search → search.query-result@1; GET /api/v1/suggest?q=&owner=&type=&limit= → { suggestions } (title prefixes, at most 20, only documents the caller may see); GET /api/v1/documents/:owner/:type/:id → { document } (one document by exact id, if the caller may see it; 404 otherwise).",
+ "anyOf": [
+ {
+ "$ref": "query-result.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "suggestions"
+ ],
+ "properties": {
+ "suggestions": {
+ "type": "array",
+ "maxItems": 20,
+ "items": {
+ "type": "object",
+ "required": [
+ "owner",
+ "type",
+ "id"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "canonical_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "document"
+ ],
+ "properties": {
+ "document": {
+ "type": "object",
+ "required": [
+ "owner",
+ "type",
+ "id",
+ "revision",
+ "indexable"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ },
+ "revision": {
+ "type": "integer"
+ },
+ "visibility": {
+ "type": "string"
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "summary": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "canonical_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "facets": {},
+ "language": {},
+ "authorship": {},
+ "provenance": {},
+ "published_at": {},
+ "updated_at": {},
+ "indexable": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
"wiki.space.updated": {
"wiki.space.updated": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "wiki",
"owner": "wiki",
@@ -12124,6 +13010,351 @@
@@ -12124,6 +13010,351 @@
]
]
}
}
},
},
+ "ai.run-citation": {
+ "version": "1.0.0",
+ "owner": "ai",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/run-citation.v1.json",
+ "title": "AiRunCitation",
+ "description": "ai.run-citation@1: one source a run cited on OpenVibe.AI (the citations table): what the workflow was given or a citation the requester attached afterwards (provenance.attached), with trust and provenance.",
+ "type": "object",
+ "required": [
+ "id",
+ "run_id",
+ "source_type"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "run_id": {
+ "type": "string"
+ },
+ "ordinal": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "source_type": {
+ "type": "string"
+ },
+ "source_id": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "author": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "published_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "retrieved_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "snippet": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "content_hash": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "trust": {
+ "type": "object"
+ },
+ "provenance": {
+ "type": "object"
+ },
+ "attached_by": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "created_at": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "ai.run-create-request": {
+ "version": "1.0.0",
+ "owner": "ai",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/run-create-request.v1.json",
+ "title": "AiRunCreateRequest",
+ "description": "ai.run-create-request@1: bodies of ai.run.create on OpenVibe.AI. POST /api/v1/runs: ai.run-request@1. POST /api/v1/chat, /generate, /summarize, /classify, /extract, /enrich and /embed: a run of workflow ai.<op> whose input is the body itself (validated by that workflow's own input schema), except idempotency_key, target, attribution, on_behalf_of and options, which are the run's. POST /api/v1/runs/:id/citations: { citations } (1-50, each with a source_type; the requester only). POST /api/v1/runs/:id/cancel and /retry take no body.",
+ "anyOf": [
+ {
+ "$ref": "run-request.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "citations"
+ ],
+ "properties": {
+ "citations": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 50,
+ "items": {
+ "type": "object",
+ "required": [
+ "source_type"
+ ],
+ "properties": {
+ "source_type": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_.-]{1,63}$"
+ },
+ "source_id": {
+ "type": "string",
+ "maxLength": 4000
+ },
+ "url": {
+ "type": "string",
+ "maxLength": 4000
+ },
+ "title": {
+ "type": "string",
+ "maxLength": 4000
+ },
+ "author": {
+ "type": "string",
+ "maxLength": 4000
+ },
+ "snippet": {
+ "type": "string",
+ "maxLength": 4000
+ },
+ "provenance": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "description": "A direct operation: the workflow input fields plus idempotency_key, target, attribution, on_behalf_of and options.",
+ "properties": {
+ "idempotency_key": {
+ "type": "string"
+ },
+ "target": {},
+ "attribution": {},
+ "on_behalf_of": {},
+ "options": {
+ "type": "object"
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "ai.run-create-result": {
+ "version": "1.0.0",
+ "owner": "ai",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/run-create-result.v1.json",
+ "title": "AiRunCreateResult",
+ "description": "ai.run-create-result@1: answers of ai.run.create on OpenVibe.AI. POST /api/v1/runs and the direct operations → { run, replayed? } (201 when it finished or was served from cache within the wait, 202 with Location while queued or running, 200 with replayed true for an idempotent repeat); POST …/cancel → { run }; POST …/retry → { run } (a new run with retry_of); POST …/citations → 201 { citations }. A quota refusal or a full queue is 429 with Retry-After.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "run"
+ ],
+ "properties": {
+ "run": {
+ "$ref": "run.v1.json"
+ },
+ "replayed": {
+ "const": true
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "citations"
+ ],
+ "properties": {
+ "citations": {
+ "type": "array",
+ "items": {
+ "$ref": "run-citation.v1.json"
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ "ai.run-read-result": {
+ "version": "1.0.0",
+ "owner": "ai",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/run-read-result.v1.json",
+ "title": "AiRunReadResult",
+ "description": "ai.run-read-result@1: answers of ai.run.read on OpenVibe.AI (the caller's own runs; usage readers may ask for all). GET /api/v1/runs?status=&workflow=&target=&limit=&before= → { runs }; GET /api/v1/runs/:id → { run, citations, requests } (the request log is metadata only: hashes, tokens, cost, latency, never prompts); GET /api/v1/runs/:id/citations → { citations }. Someone else's run is 404 run.not_found.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "runs"
+ ],
+ "properties": {
+ "runs": {
+ "type": "array",
+ "items": {
+ "$ref": "run.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "run",
+ "citations",
+ "requests"
+ ],
+ "properties": {
+ "run": {
+ "$ref": "run.v1.json"
+ },
+ "citations": {
+ "type": "array",
+ "items": {
+ "$ref": "run-citation.v1.json"
+ }
+ },
+ "requests": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "id",
+ "seq",
+ "status"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "seq": {
+ "type": "integer"
+ },
+ "operation": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "provider_key": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "model_key": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "status": {
+ "type": "string"
+ },
+ "fallback": {},
+ "tokens_in": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tokens_out": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "cost_usd": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "latency_ms": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "citations"
+ ],
+ "properties": {
+ "citations": {
+ "type": "array",
+ "items": {
+ "$ref": "run-citation.v1.json"
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
"tips.interaction.ready": {
"tips.interaction.ready": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "tips",
"owner": "tips",
@@ -23809,6 +25040,33 @@
@@ -23809,6 +25040,33 @@
}
}
}
}
},
},
+ "network.module-write-request": {
+ "version": "1.0.0",
+ "owner": "network",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/network/module-write-request.v1.json",
+ "title": "NetworkModuleWriteRequest",
+ "description": "network.module-write-request@1: the request of network.modules.write on OpenVibe.Network. PUT /internal/modules/:namespace/:subject: { data } — the record's new value, validated against the namespace's schema; If-Match names the revision read (412 if it moved). DELETE /internal/modules/:namespace/:subject (204) takes no body. The answer to PUT is modules.module-record@1.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "data"
+ ],
+ "properties": {
+ "data": {
+ "type": "object"
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
"tools.tool": {
"tools.tool": {
"version": "1.1.0",
"version": "1.1.0",
"owner": "tools",
"owner": "tools",
@@ -25253,6 +26511,99 @@
@@ -25253,6 +26511,99 @@
}
}
}
}
},
},
+ "tools.job-create-request": {
+ "version": "1.0.0",
+ "owner": "tools",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/tools/job-create-request.v1.json",
+ "title": "ToolsJobCreateRequest",
+ "description": "tools.job-create-request@1: the request of tools.job.create on the Tools satellites that run jobs. POST /api/v1/jobs: tools.job-request@1 (JSON or multipart). POST /api/v1/jobs/:id/retry and PUT|DELETE /api/v1/jobs/:id/references/:ref take no body.",
+ "anyOf": [
+ {
+ "$ref": "job-request.v1.json"
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "tools.job-read-result": {
+ "version": "1.0.0",
+ "owner": "tools",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/tools/job-read-result.v1.json",
+ "title": "ToolsJobReadResult",
+ "description": "tools.job-read-result@1: answers of tools.job.read on the Tools satellites. GET /api/v1/jobs/:id → tools.job@1, and so is the data of every event on GET /api/v1/jobs/:id/events (Server-Sent Events, Last-Event-ID resume); GET /api/v1/jobs/:id/files/:n → the result file's bytes (Content-Type of the file; 409 tools.job.not_ready before the job succeeded).",
+ "anyOf": [
+ {
+ "$ref": "job.v1.json"
+ },
+ {
+ "type": "string",
+ "contentEncoding": "binary",
+ "description": "GET /api/v1/jobs/:id/files/:n."
+ }
+ ]
+ }
+ },
+ "tools.tool-read-result": {
+ "version": "1.0.0",
+ "owner": "tools",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/tools/tool-read-result.v1.json",
+ "title": "ToolsToolReadResult",
+ "description": "tools.tool-read-result@1: answers of tools.tool.read on openvibe.tools (public registry; ADR-027). GET /api/v1/tools → tools.tool-list@1; GET /api/v1/tools/:id → tools.tool@1 with its schemas embedded; GET /api/v1/tools/:id/schema → the tool's own JSON Schema document { $schema, $id, $defs: { input, output } } (application/schema+json; input is false for a tool without an API input).",
+ "anyOf": [
+ {
+ "$ref": "tool-list.v1.json"
+ },
+ {
+ "$ref": "tool.v1.json"
+ },
+ {
+ "type": "object",
+ "required": [
+ "$schema",
+ "$id",
+ "$defs"
+ ],
+ "properties": {
+ "$schema": {
+ "type": "string"
+ },
+ "$id": {
+ "type": "string"
+ },
+ "$defs": {
+ "type": "object",
+ "required": [
+ "input",
+ "output"
+ ],
+ "properties": {
+ "input": {
+ "type": [
+ "object",
+ "boolean"
+ ]
+ },
+ "output": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
"news.source.ingested": {
"news.source.ingested": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "news",
"owner": "news",
@@ -41825,6 +43176,94 @@
@@ -41825,6 +43176,94 @@
}
}
}
}
},
},
+ "media.file-read-result": {
+ "version": "1.0.0",
+ "owner": "media",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/media/file-read-result.v1.json",
+ "title": "MediaFileReadResult",
+ "description": "media.file-read-result@1: answers of media.object.read on OpenVibe.Media. GET /api/v1/:app/files/:key → media.file@1 (the file's metadata; 404 when it is not the tenant's); GET /f/:key → the bytes, with Content-Type and Range (a sandbox file only through its signed URL).",
+ "anyOf": [
+ {
+ "$ref": "file.v1.json"
+ },
+ {
+ "type": "string",
+ "contentEncoding": "binary",
+ "description": "GET /f/:key: the file."
+ }
+ ]
+ }
+ },
+ "media.file": {
+ "version": "1.0.0",
+ "owner": "media",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/media/file.v1.json",
+ "title": "MediaFile",
+ "description": "media.file@1: one stored file of a tenant on OpenVibe.Media (server/files/routes.js filePublic): its key, owner, name, size, type and hash, and the URL it is served from (/f/<key>; a developer-project sandbox file gets a short-lived signed URL instead).",
+ "type": "object",
+ "required": [
+ "key",
+ "app_id",
+ "original_name",
+ "size",
+ "mime",
+ "url",
+ "created_at"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "app_id": {
+ "type": "string"
+ },
+ "user_id": {
+ "type": [
+ "string",
+ "integer",
+ "null"
+ ]
+ },
+ "original_name": {
+ "type": "string"
+ },
+ "size": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "mime": {
+ "type": "string"
+ },
+ "sha256": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "url": {
+ "type": "string"
+ },
+ "sandbox": {
+ "const": true
+ },
+ "url_expires_at": {
+ "type": [
+ "string",
+ "integer"
+ ]
+ },
+ "created_at": {
+ "type": "string"
+ }
+ }
+ }
+ },
"community.paste.created": {
"community.paste.created": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
@@ -45139,106 +46578,7 @@
@@ -45139,106 +46578,7 @@
"type": "boolean"
"type": "boolean"
},
},
"item": {
"item": {
- "type": "object",
- "required": [
- "id",
- "source",
- "title",
- "url",
- "origin",
- "label",
- "actor",
- "occurred_at"
- ],
- "properties": {
- "id": {
- "type": "integer"
- },
- "source": {
- "type": "object",
- "required": [
- "service",
- "type",
- "id"
- ],
- "properties": {
- "service": {
- "type": "string"
- },
- "type": {
- "type": "string"
- },
- "id": {
- "type": "string"
- }
- }
- },
- "title": {
- "type": "string",
- "maxLength": 300
- },
- "url": {
- "type": "string"
- },
- "origin": {
- "enum": [
- "user",
- "ai",
- "system"
- ]
- },
- "label": {
- "enum": [
- "AI",
- "System",
- null
- ]
- },
- "actor": {
- "type": [
- "object",
- "null"
- ],
- "properties": {
- "subject": {
- "type": [
- "string",
- "null"
- ]
- },
- "username": {
- "type": [
- "string",
- "null"
- ]
- },
- "display_name": {
- "type": [
- "string",
- "null"
- ]
- },
- "avatar_url": {
- "type": [
- "string",
- "null"
- ]
- },
- "profile_color": {
- "type": [
- "string",
- "null"
- ]
- }
- }
- },
- "occurred_at": {
- "type": [
- "string",
- "null"
- ]
- }
- }
+ "$ref": "pulse-item.v1.json"
}
}
}
}
},
},
@@ -45896,6 +47236,953 @@
@@ -45896,6 +47236,953 @@
]
]
}
}
},
},
+ "community.comment-thread": {
+ "version": "1.0.0",
+ "owner": "community",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/comment-thread.v1.json",
+ "title": "CommunityCommentThread",
+ "description": "community.comment-thread@1: one embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeThread): the entity it belongs to, its visibility and comment count. id is the sequential id for services and the access id (cth_…) for everyone else; a hidden thread shows moderators its count, others only that it is hidden.",
+ "type": "object",
+ "required": [
+ "id",
+ "access_id",
+ "ref",
+ "visibility",
+ "comment_count"
+ ],
+ "properties": {
+ "id": {
+ "type": [
+ "integer",
+ "string"
+ ]
+ },
+ "access_id": {
+ "type": "string",
+ "pattern": "^cth_"
+ },
+ "ref": {
+ "$ref": "../common/entity-ref.v1.json"
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "hidden",
+ "locked"
+ ]
+ },
+ "comment_count": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "community.comment-write-request": {
+ "version": "1.0.0",
+ "owner": "community",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/comment-write-request.v1.json",
+ "title": "CommunityCommentWriteRequest",
+ "description": "community.comment-write-request@1: bodies of community.comment.write on OpenVibe.Community (people, anonymous people with a name where the thread allows it, and services acting as X-OV-Subject or as AI). POST /api/v1/comments/threads/resolve: { ref } — get or create the thread of an entity (browsers only for the allow-listed entity types; a label is taken only from a service). POST /api/v1/comments/threads/:id/comments: { message, parent_id?, anon_name? } (message trimmed, at most 5000 characters; a reply to a reply joins the top-level comment). POST /api/v1/comments/:commentId/votes: { value: 1 | -1 | 0 } (people). GET /api/v1/comments/threads/:id and DELETE /api/v1/comments/:commentId take no body. Unknown fields are ignored.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "ref"
+ ],
+ "properties": {
+ "ref": {
+ "$ref": "../common/entity-ref.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "message"
+ ],
+ "properties": {
+ "message": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 5000,
+ "pattern": "\\S"
+ },
+ "parent_id": {
+ "type": [
+ "integer",
+ "string",
+ "null"
+ ]
+ },
+ "anon_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "value"
+ ],
+ "properties": {
+ "value": {
+ "enum": [
+ 1,
+ -1,
+ 0,
+ "1",
+ "-1",
+ "0"
+ ]
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "community.comment-write-result": {
+ "version": "1.0.0",
+ "owner": "community",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/comment-write-result.v1.json",
+ "title": "CommunityCommentWriteResult",
+ "description": "community.comment-write-result@1: answers of community.comment.write on OpenVibe.Community. POST …/threads/resolve → { thread, created } (201 when created); GET …/threads/:id?after=&sort=&limit=&parent= → { thread, comments, next_cursor, viewer }; POST …/threads/:id/comments → 201 { comment }; DELETE /api/v1/comments/:commentId → { ok, id }; POST …/:commentId/votes → { comment_id, score, upvotes, downvotes, my_vote }. Refusals are problem+json (thread.locked, auth.required, community.blocked, …).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "thread",
+ "created"
+ ],
+ "properties": {
+ "thread": {
+ "$ref": "comment-thread.v1.json"
+ },
+ "created": {
+ "type": "boolean"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "thread",
+ "comments",
+ "next_cursor",
+ "viewer"
+ ],
+ "properties": {
+ "thread": {
+ "$ref": "comment-thread.v1.json"
+ },
+ "comments": {
+ "type": "array",
+ "items": {
+ "$ref": "comment.v1.json"
+ }
+ },
+ "next_cursor": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "viewer": {
+ "type": "object",
+ "properties": {
+ "signed_in": {
+ "type": "boolean"
+ },
+ "can_comment": {
+ "type": "boolean"
+ },
+ "can_vote": {
+ "type": "boolean"
+ },
+ "can_moderate": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "comment"
+ ],
+ "properties": {
+ "comment": {
+ "$ref": "comment.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "ok",
+ "id"
+ ],
+ "properties": {
+ "ok": {
+ "const": true
+ },
+ "id": {
+ "type": "integer"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "comment_id",
+ "score",
+ "upvotes",
+ "downvotes",
+ "my_vote"
+ ],
+ "properties": {
+ "comment_id": {
+ "type": "integer"
+ },
+ "score": {
+ "type": "integer"
+ },
+ "upvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "downvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "my_vote": {
+ "enum": [
+ 1,
+ -1,
+ 0
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ "community.comment": {
+ "version": "1.0.0",
+ "owner": "community",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/comment.v1.json",
+ "title": "CommunityComment",
+ "description": "community.comment@1: one comment in an embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeComment), with its author (or anonymous name), score and the caller's vote, reply count and, for top-level comments in a page, the first replies. A deleted comment keeps its place with no author or message.",
+ "type": "object",
+ "required": [
+ "id",
+ "thread_id",
+ "parent_id",
+ "origin",
+ "author",
+ "message",
+ "deleted",
+ "score",
+ "my_vote",
+ "can_edit",
+ "can_delete"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "thread_id": {
+ "type": [
+ "integer",
+ "string"
+ ]
+ },
+ "parent_id": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "origin": {
+ "type": "string"
+ },
+ "author": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "subject": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "username": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "display_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "avatar_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "profile_color": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "is_ai": {
+ "const": true
+ },
+ "is_system": {
+ "const": true
+ }
+ }
+ },
+ "anon_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "display_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "message": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 5000
+ },
+ "deleted": {
+ "type": "boolean"
+ },
+ "score": {
+ "type": "integer"
+ },
+ "upvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "downvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "my_vote": {
+ "enum": [
+ 1,
+ -1,
+ 0
+ ]
+ },
+ "reply_count": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "edited_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "can_edit": {
+ "type": "boolean"
+ },
+ "can_delete": {
+ "type": "boolean"
+ },
+ "replies": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ },
+ "community.forum-space": {
+ "version": "1.0.0",
+ "owner": "community",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/forum-space.v1.json",
+ "title": "CommunityForumSpace",
+ "description": "community.forum-space@1: one forum space as OpenVibe.Community's /api/v1/spaces API shows it (server/forum/service.js shapeSpace): its slug, name, visibility, thread kind and statuses, style, whether votes and reactions are on, its group and parent board, the members-only gate and, in lists, its counts and last activity.",
+ "type": "object",
+ "required": [
+ "slug",
+ "name",
+ "visibility",
+ "url",
+ "thread_kind",
+ "statuses",
+ "style",
+ "votes",
+ "reactions",
+ "group",
+ "parent",
+ "members_only"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "members",
+ "staff"
+ ]
+ },
+ "url": {
+ "type": "string"
+ },
+ "thread_kind": {
+ "type": "string"
+ },
+ "statuses": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "style": {
+ "enum": [
+ "feed",
+ "forum"
+ ]
+ },
+ "votes": {
+ "type": "boolean"
+ },
+ "reactions": {
+ "type": "boolean"
+ },
+ "group": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ },
+ "parent": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ },
+ "post_count": {
+ "type": "integer"
+ },
+ "thread_count": {
+ "type": "integer"
+ },
+ "last_activity_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "members_only": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "owner_username": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "join_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ },
+ "last_post": {
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "children": {
+ "type": "array"
+ }
+ }
+ }
+ },
+ "community.pulse-item": {
+ "version": "1.0.0",
+ "owner": "community",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/pulse-item.v1.json",
+ "title": "CommunityPulseItem",
+ "description": "community.pulse-item@1: one item of the network's public activity on OpenVibe.Community (server/pulse/service.js shape), with provenance: its source entity, title, url, origin (user, ai or system), the AI or System label, the actor (a person for user items, never for AI or system ones) and when it happened.",
+ "type": "object",
+ "required": [
+ "id",
+ "source",
+ "title",
+ "url",
+ "origin",
+ "label",
+ "actor",
+ "occurred_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "source": {
+ "type": "object",
+ "required": [
+ "service",
+ "type",
+ "id"
+ ],
+ "properties": {
+ "service": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ }
+ }
+ },
+ "title": {
+ "type": "string",
+ "maxLength": 300
+ },
+ "url": {
+ "type": "string"
+ },
+ "origin": {
+ "enum": [
+ "user",
+ "ai",
+ "system"
+ ]
+ },
+ "label": {
+ "enum": [
+ "AI",
+ "System",
+ null
+ ]
+ },
+ "actor": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "subject": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "username": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "display_name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "avatar_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "profile_color": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "is_ai": {
+ "const": true
+ },
+ "is_system": {
+ "const": true
+ }
+ }
+ },
+ "occurred_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "community.pulse-read-result": {
+ "version": "1.0.0",
+ "owner": "community",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/pulse-read-result.v1.json",
+ "title": "CommunityPulseReadResult",
+ "description": "community.pulse-read-result@1: answers of community.pulse.read on OpenVibe.Community (anyone). GET /api/v1/pulse?origin=user|ai|system&after=&limit= → { items, next_cursor } (newest first, at most 100; an unknown origin or a cursor this API did not issue is 400). GET /pulse is the same activity as an HTML page.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "items",
+ "next_cursor"
+ ],
+ "properties": {
+ "items": {
+ "type": "array",
+ "maxItems": 100,
+ "items": {
+ "$ref": "pulse-item.v1.json"
+ }
+ },
+ "next_cursor": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ },
+ {
+ "type": "string",
+ "contentMediaType": "text/html",
+ "description": "GET /pulse: the HTML page."
+ }
+ ]
+ }
+ },
+ "community.space-read-result": {
+ "version": "1.0.0",
+ "owner": "community",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/space-read-result.v1.json",
+ "title": "CommunitySpaceReadResult",
+ "description": "community.space-read-result@1: answers of community.space.read on OpenVibe.Community. GET /api/v1/spaces → { spaces, groups } (the spaces the caller can open, each with its last post, and the board index: groups in order with their top-level spaces and child boards); GET /api/v1/spaces/:space → { space } (a staff or members-only space the caller cannot open is 404 or 401/403 vip.members_only); GET /s is the board index as an HTML page.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "spaces",
+ "groups"
+ ],
+ "properties": {
+ "spaces": {
+ "type": "array",
+ "items": {
+ "$ref": "forum-space.v1.json"
+ }
+ },
+ "groups": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "name",
+ "spaces"
+ ],
+ "properties": {
+ "slug": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "spaces": {
+ "type": "array"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "space"
+ ],
+ "properties": {
+ "space": {
+ "$ref": "forum-space.v1.json"
+ }
+ }
+ },
+ {
+ "type": "string",
+ "contentMediaType": "text/html",
+ "description": "GET /s: the board index page."
+ }
+ ]
+ }
+ },
+ "community.thread-read-result": {
+ "version": "1.0.0",
+ "owner": "community",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/community/thread-read-result.v1.json",
+ "title": "CommunityThreadReadResult",
+ "description": "community.thread-read-result@1: answers of community.thread.read on OpenVibe.Community. GET /api/v1/spaces/:space/threads?sort=&page=&limit=&category=&status= → a page of threads with the space, paging, categories, children and what the viewer may do; GET /api/v1/spaces/:space/threads/:slug?page= → the thread with a page of posts (reactions, author stats, crosspost info, attachments settings, paging, viewer). GET /s/:space and /s/:space/t/:slug are the same as HTML pages; GET /s/feed.xml and /s/:space/feed.xml are RSS feeds of public threads.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "space",
+ "threads",
+ "page",
+ "pages",
+ "total"
+ ],
+ "properties": {
+ "space": {
+ "type": "object"
+ },
+ "sort": {
+ "type": "string"
+ },
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "per_page": {
+ "type": "integer"
+ },
+ "total": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "pages": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "categories": {
+ "type": "array"
+ },
+ "category": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "status": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "viewer": {
+ "type": "object"
+ },
+ "children": {
+ "type": "array"
+ },
+ "groups": {
+ "type": "array"
+ },
+ "threads": {
+ "type": "array",
+ "items": {
+ "$ref": "forum-thread.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "space",
+ "thread",
+ "posts",
+ "page",
+ "pages"
+ ],
+ "properties": {
+ "space": {
+ "type": "object"
+ },
+ "thread": {
+ "$ref": "forum-thread.v1.json"
+ },
+ "posts": {
+ "type": "array",
+ "items": {
+ "$ref": "forum-post.v1.json"
+ }
+ },
+ "reactions": {
+ "type": "array"
+ },
+ "crosspost": {
+ "type": "object"
+ },
+ "categories": {
+ "type": "array"
+ },
+ "attachments": {
+ "type": "object"
+ },
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "per_page": {
+ "type": "integer"
+ },
+ "pages": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "total": {
+ "type": "integer"
+ },
+ "viewer": {
+ "type": "object"
+ }
+ }
+ },
+ {
+ "type": "string",
+ "contentMediaType": "text/html",
+ "description": "GET /s/:space and /s/:space/t/:slug: HTML pages."
+ },
+ {
+ "type": "string",
+ "contentMediaType": "application/rss+xml",
+ "description": "GET /s/feed.xml and /s/:space/feed.xml: RSS feeds."
+ }
+ ]
+ }
+ },
"openre.destination": {
"openre.destination": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "openre",
"owner": "openre",
@@ -90,6 +90,60 @@ export type ServiceTokenClaims = {
@@ -90,6 +90,60 @@ export type ServiceTokenClaims = {
[k: string]: unknown | undefined;
[k: string]: unknown | undefined;
};
};
+/** [email protected] (owner: network) */
+/**
+ * identity.resolve-request@1: the request of identity.subject.resolve on OpenVibe.Network. GET /internal/identity/resolve?subject_id= or ?system=&id=[&type=user] takes no body. POST /internal/identity/resolve-batch: { subject_ids } or { system, type?, ids } (at most 500; 413 beyond).
+ */
+export type IdentityResolveRequest =
+ | {
+ /**
+ * @minItems 1
+ * @maxItems 500
+ */
+ subject_ids: [string, ...string[]];
+ }
+ | {
+ system: string;
+ type?: string;
+ /**
+ * @minItems 1
+ * @maxItems 500
+ */
+ ids: [string | number, ...(string | number)[]];
+ }
+ | NoBody;
+
+/** [email protected] (owner: network) */
+/**
+ * identity.resolve-result@1: answers of identity.subject.resolve on OpenVibe.Network. GET /internal/identity/resolve → one identity.subject-projection@1 (404 identity.subject_not_found when nothing matches); POST /internal/identity/resolve-batch → { results } keyed by each id asked, null for an id that resolves to nothing.
+ */
+export type IdentityResolveResult =
+ | IdentitySubjectProjection
+ | {
+ results: {
+ [k: string]: (IdentitySubjectProjection | null) | undefined;
+ };
+ };
+
+/** [email protected] (owner: network) */
+/**
+ * identity.subject-projection@1: what OpenVibe.Network's identity resolver answers about a subject (server/identity/subjects.js resolve): the SubjectRef, the Network account behind it, its display fields, whether it is banned, and every legacy id mapped to it.
+ */
+export interface IdentitySubjectProjection {
+ subject: SubjectRef;
+ network_user_id?: number;
+ network_anon_id?: number;
+ username: string;
+ display_name: string;
+ avatar_url: string | null;
+ banned: boolean;
+ legacy_ids?: {
+ source_system: string;
+ source_type: string;
+ source_id: string;
+ }[];
+}
+
/** [email protected] (owner: contracts) */
/** [email protected] (owner: contracts) */
/**
/**
* Typed reference to another service's entity. Store this instead of a foreign key; resolve a display projection from the owner.
* Typed reference to another service's entity. Store this instead of a foreign key; resolve a display projection from the owner.
@@ -615,7 +669,7 @@ export type Binary = string;
@@ -615,7 +669,7 @@ export type Binary = string;
/** [email protected] (owner: media) */
/** [email protected] (owner: media) */
/**
/**
- * media.file-upload@1: the multipart/form-data body of POST /api/v1/:app/files (media.object.upload, namespace :app): one part named `file` with the bytes and its filename and type. The tenant's quota is checked against the bytes; the answer is media.media-ref@1.
+ * media.file-upload@1: the multipart/form-data body of POST /api/v1/:app/files (media.object.upload, namespace :app): one part named `file` with the bytes and its filename and type. The tenant's quota is checked against the bytes; the answer is media.file@1 (201).
*/
*/
export interface MediaFileUpload {
export interface MediaFileUpload {
/**
/**
@@ -5826,6 +5880,26 @@ export type CodesReleaseReadResult =
@@ -5826,6 +5880,26 @@ export type CodesReleaseReadResult =
manifest: AppManifest | ModManifest | null;
manifest: AppManifest | ModManifest | null;
};
};
+/** [email protected] (owner: codes) */
+/**
+ * codes.release-manage-request@1: bodies of codes.release.manage on OpenVibe.Codes (an app token managing its own releases). POST /api/v1/apps/:app/releases: { kind?, manifest, notes?, publish? } — kind mod or app (anything else is app); manifest must validate as codes.app-manifest@1 for an app or mods.mod-manifest@1 for a mod (422 manifest.invalid with the validation otherwise; a version that already has a release is 409); notes are cut to 2000 characters; publish true publishes the draft at once. POST /api/v1/releases/:id/deprecate: { reason, replacement? } (a reason is required, cut to 500 characters; replacement must be a published release of the same app). POST /api/v1/releases/:id/revoke: { reason } (required). POST /api/v1/releases/:id/publish takes no body. Unknown fields are ignored.
+ */
+export type CodesReleaseManageRequest =
+ | {
+ /**
+ * mod, or app (the default).
+ */
+ kind?: string;
+ manifest: AppManifest | ModManifest;
+ notes?: string | null;
+ publish?: boolean;
+ }
+ | {
+ reason: string;
+ replacement?: string | null;
+ }
+ | NoBody;
+
/** [email protected] (owner: events) */
/** [email protected] (owner: events) */
/**
/**
* payload.redacts (ADR-026): a producer takes back events it published earlier. Any event may carry it, normally the producer's own *.deleted event. It names event_ids, or subject_type + subject_ids, or both, never an empty list. event_ids names events directly; subject_type + subject_ids names every stored event of the same source (for an app, the same project and environment) about those subjects. OpenVibe.Events rewrites each target into a tombstone (events.tombstone-payload@1) in the transaction that stores the directive. Naming another source's event is 403 events.redaction_not_allowed (the whole batch is refused); a malformed directive is 422 events.invalid_redaction. An event that carries a directive is never redacted itself.
* payload.redacts (ADR-026): a producer takes back events it published earlier. Any event may carry it, normally the producer's own *.deleted event. It names event_ids, or subject_type + subject_ids, or both, never an empty list. event_ids names events directly; subject_type + subject_ids names every stored event of the same source (for an app, the same project and environment) about those subjects. OpenVibe.Events rewrites each target into a tombstone (events.tombstone-payload@1) in the transaction that stores the directive. Naming another source's event is 403 events.redaction_not_allowed (the whole batch is refused); a malformed directive is 422 events.invalid_redaction. An event that carries a directive is never redacted itself.
@@ -6036,6 +6110,20 @@ export interface EventsSubscription {
@@ -6036,6 +6110,20 @@ export interface EventsSubscription {
secret?: string;
secret?: string;
}
}
+/** [email protected] (owner: events) */
+/**
+ * events.publish-request@1: the body of POST /api/v1/events on OpenVibe.Events (events.event.publish for services, events.app.publish for developer apps): one events.event-envelope@1, or a batch { events: [...] } of 1 to 100 envelopes stored all or nothing. trace_id, priority (important) and visibility (internal) default when left out. source must be the caller and event_type one of its prefixes (an app: app-<ULID> and app.<project_key>.*, actor the app or the user it acts for).
+ */
+export type EventsPublishRequest =
+ | EventEnvelope
+ | {
+ /**
+ * @minItems 1
+ * @maxItems 100
+ */
+ events: [EventEnvelope, ...EventEnvelope[]];
+ };
+
/** [email protected] (owner: chat) */
/** [email protected] (owner: chat) */
/**
/**
* chat.message.deleted v1 (OpenVibe.Chat server/db/database.js _announceDeleted). Public messages were deleted: by a moderator, through Live's bridge, the author's or an anon's history, a time-range purge or the auto-delete sweep. Envelope: subject { type: chat_message, id: <first id> }, visibility public, priority important, actor service:chat. One event per 500 ids. Carries only ids, never the text, the author or who deleted it; payload.redacts makes OpenVibe.Events tombstone the chat.message.created of each id.
* chat.message.deleted v1 (OpenVibe.Chat server/db/database.js _announceDeleted). Public messages were deleted: by a moderator, through Live's bridge, the author's or an anon's history, a time-range purge or the auto-delete sweep. Envelope: subject { type: chat_message, id: <first id> }, visibility public, priority important, actor service:chat. One event per 500 ids. Carries only ids, never the text, the author or who deleted it; payload.redacts makes OpenVibe.Events tombstone the chat.message.created of each id.
@@ -9789,102 +9877,1754 @@ export type SourcesSourceWriteRequest =
@@ -9789,102 +9877,1754 @@ export type SourcesSourceWriteRequest =
string
string
]
]
| [
| [
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ];
+ /**
+ * A date the source states.
+ */
+ published_at?: string | null;
+ updated_at?: string | null;
+ fields?: {
+ [k: string]: (string | number | boolean | null) | undefined;
+ };
+ }
+ | {
+ reason: string;
+ }
+ | NoBody;
+
+/** [email protected] (owner: sources) */
+/**
+ * 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).
+ */
+export type SourcesItemReadResult =
+ | {
+ /**
+ * @maxItems 500
+ */
+ items: SourceItem[];
+ next_after: number;
+ more?: boolean;
+ sources: {
+ [k: string]:
+ | {
+ status?: string;
+ stale?: boolean;
+ last_success_at?: string | null;
+ }
+ | undefined;
+ };
+ }
+ | {
+ item: SourceItem;
+ source: {
+ key: string;
+ status?: string;
+ stale?: boolean;
+ last_success_at?: string | null;
+ } | null;
+ };
+
+/** [email protected] (owner: sources) */
+/**
+ * sources.source-read-result@1: answers of sources.source.read on OpenVibe.Sources. GET /api/v1/sources → { sources }; GET /api/v1/sources/:key → { source } (sources.source@1 with health); GET /api/v1/sources/:key/runs?before=&limit= → { runs, next_before } (newest first); GET /api/v1/runs?after=&source=&state=&limit= → { runs, next_after } (every source's fetch runs in order; state failed means any failure); GET /api/v1/health → { counts, attention } (sources by health status, and the failing, stale or never fetched ones).
+ */
+export type SourcesSourceReadResult =
+ | {
+ sources: Source[];
+ }
+ | {
+ source: Source;
+ }
+ | {
+ [k: string]: unknown | undefined;
+ }
+ | {
+ counts: {
+ [k: string]: number | undefined;
+ };
+ attention: {
+ key: string;
+ category?: string;
+ type?: string;
+ status: string;
+ }[];
+ };
+
+/** [email protected] (owner: search) */
+/**
+ * search.document.indexed v1 (OpenVibe.Search server/store.js announce). An applied index document (from an owner's *.index_document.upserted or the direct API) is servable: restricted (ACL only) or public. Sent on every applied revision that stays exposed, first time or not. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.
+ */
+export interface SearchDocumentIndexedPayload {
+ owner: string;
+ type: string;
+ id: string;
+ revision: number;
+ exposure: "restricted" | "public_unlisted" | "public_listed";
+ /**
+ * true when Search already held an earlier revision of the document.
+ */
+ reindexed: boolean;
+ /**
+ * Only when the document is public (public_unlisted or public_listed); null otherwise or when it has none.
+ */
+ canonical_url: string | null;
+}
+
+/** [email protected] (owner: search) */
+/**
+ * search.document.removed v1 (OpenVibe.Search server/store.js announce). A document's exposure went down: deleted, made draft or unpublished, its visibility changed, or it became noindex. Caches and sitemaps purge canonical_url. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.
+ */
+export interface SearchDocumentRemovedPayload {
+ owner: string;
+ type: string;
+ id: string;
+ revision: number;
+ reason: "deleted" | "draft" | "not_published" | "visibility_changed" | "noindex";
+ previous_exposure: "restricted" | "public_unlisted" | "public_listed";
+ /**
+ * Lower than previous_exposure. none: no longer served to anyone.
+ */
+ exposure: "none" | "restricted" | "public_unlisted";
+ /**
+ * The URL only when the document was public before (so it must be purged); null otherwise.
+ */
+ canonical_url: string | null;
+}
+
+/** [email protected] (owner: search) */
+/**
+ * search.document-write-request@1: the request of search.document.write on OpenVibe.Search (a service writes and reads only the documents it owns). PUT /api/v1/documents/:owner/:type/:id: a search.index-document@1 (owner, type and id come from the path; a body that names others is 422). DELETE /api/v1/documents/:owner/:type/:id?revision= (a tombstone) and the owner reads (GET /api/v1/owners/:owner/documents, …/documents/:type/:id, …/rejections) take no body.
+ */
+export type SearchDocumentWriteRequest = IndexDocument | NoBody;
+
+/** [email protected] (owner: search) */
+/**
+ * search.owner-result@1: answers of search.document.write on OpenVibe.Search. PUT and DELETE /api/v1/documents/:owner/:type/:id → search.write-result@1 (stale and conflicting revisions are 409 problems); GET /api/v1/owners/:owner/documents?type=&after=&limit= → { owner, documents, next_after } (reconciliation: type, id, revision, deleted, exposure, hash, indexed_at); GET /api/v1/owners/:owner/documents/:type/:id → { document, effective_indexability, exposure, via, event_id, indexed_at } (the stored document); GET /api/v1/owners/:owner/rejections?after= → { rejections, next_after } (index-document events Search refused, at most 200).
+ */
+export type SearchOwnerResult =
+ | SearchWriteResult
+ | {
+ owner: string;
+ documents: {
+ type: string;
+ id: string;
+ revision: number;
+ deleted: boolean;
+ exposure?: unknown;
+ hash?: string | null;
+ indexed_at?: string | null;
+ }[];
+ next_after: number | null;
+ }
+ | {
+ document: IndexDocument;
+ effective_indexability: unknown;
+ exposure?: unknown;
+ via?: unknown;
+ event_id?: string | null;
+ indexed_at?: string | null;
+ }
+ | {
+ /**
+ * @maxItems 200
+ */
+ rejections: {
+ seq: number;
+ event_id?: string | null;
+ event_type?: string | null;
+ type?: string | null;
+ id?: string | null;
+ revision?: number | null;
+ code: string;
+ detail?: string | null;
+ at?: string | null;
+ }[];
+ next_after: number | null;
+ };
+
+/** [email protected] (owner: search) */
+/**
+ * search.read-result@1: answers of search.query.run and search.query.delegate on OpenVibe.Search. GET /api/v1/search → search.query-result@1; GET /api/v1/suggest?q=&owner=&type=&limit= → { suggestions } (title prefixes, at most 20, only documents the caller may see); GET /api/v1/documents/:owner/:type/:id → { document } (one document by exact id, if the caller may see it; 404 otherwise).
+ */
+export type SearchReadResult =
+ | SearchQueryResult
+ | {
+ /**
+ * @maxItems 20
+ */
+ suggestions:
+ | []
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
+ ]
+ | [
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
]
]
| [
| [
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string,
- string
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ },
+ {
+ owner: string;
+ type: string;
+ id: string;
+ title?: string | null;
+ canonical_url?: string | null;
+ }
];
];
- /**
- * A date the source states.
- */
- published_at?: string | null;
- updated_at?: string | null;
- fields?: {
- [k: string]: (string | number | boolean | null) | undefined;
- };
}
}
| {
| {
- reason: string;
- }
- | NoBody;
-
-/** [email protected] (owner: search) */
-/**
- * search.document.indexed v1 (OpenVibe.Search server/store.js announce). An applied index document (from an owner's *.index_document.upserted or the direct API) is servable: restricted (ACL only) or public. Sent on every applied revision that stays exposed, first time or not. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.
- */
-export interface SearchDocumentIndexedPayload {
- owner: string;
- type: string;
- id: string;
- revision: number;
- exposure: "restricted" | "public_unlisted" | "public_listed";
- /**
- * true when Search already held an earlier revision of the document.
- */
- reindexed: boolean;
- /**
- * Only when the document is public (public_unlisted or public_listed); null otherwise or when it has none.
- */
- canonical_url: string | null;
-}
-
-/** [email protected] (owner: search) */
-/**
- * search.document.removed v1 (OpenVibe.Search server/store.js announce). A document's exposure went down: deleted, made draft or unpublished, its visibility changed, or it became noindex. Caches and sitemaps purge canonical_url. Envelope: subject { type: document, id: <owner>/<type>/<id>, revision }, visibility internal, priority important, actor service:search, trace_id of the request or event that caused it.
- */
-export interface SearchDocumentRemovedPayload {
- owner: string;
- type: string;
- id: string;
- revision: number;
- reason: "deleted" | "draft" | "not_published" | "visibility_changed" | "noindex";
- previous_exposure: "restricted" | "public_unlisted" | "public_listed";
- /**
- * Lower than previous_exposure. none: no longer served to anyone.
- */
- exposure: "none" | "restricted" | "public_unlisted";
- /**
- * The URL only when the document was public before (so it must be purged); null otherwise.
- */
- canonical_url: string | null;
-}
+ document: {
+ owner: string;
+ type: string;
+ id: string;
+ revision: number;
+ visibility?: string;
+ title?: string | null;
+ summary?: string | null;
+ canonical_url?: string | null;
+ facets?: unknown;
+ language?: unknown;
+ authorship?: unknown;
+ provenance?: unknown;
+ published_at?: unknown;
+ updated_at?: unknown;
+ indexable: boolean;
+ };
+ };
/** [email protected] (owner: wiki) */
/** [email protected] (owner: wiki) */
/**
/**
@@ -13039,6 +14779,112 @@ export type AiUsageResult =
@@ -13039,6 +14779,112 @@ export type AiUsageResult =
}[];
}[];
};
};
+/** [email protected] (owner: ai) */
+/**
+ * ai.run-citation@1: one source a run cited on OpenVibe.AI (the citations table): what the workflow was given or a citation the requester attached afterwards (provenance.attached), with trust and provenance.
+ */
+export interface AiRunCitation {
+ id: number;
+ run_id: string;
+ ordinal?: number | null;
+ source_type: string;
+ source_id?: string | null;
+ url?: string | null;
+ title?: string | null;
+ author?: string | null;
+ published_at?: string | null;
+ retrieved_at?: string | null;
+ snippet?: string | null;
+ content_hash?: string | null;
+ trust?: {};
+ provenance?: {};
+ attached_by?: string | null;
+ created_at?: string;
+}
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.run-create-request@1: bodies of ai.run.create on OpenVibe.AI. POST /api/v1/runs: ai.run-request@1. POST /api/v1/chat, /generate, /summarize, /classify, /extract, /enrich and /embed: a run of workflow ai.<op> whose input is the body itself (validated by that workflow's own input schema), except idempotency_key, target, attribution, on_behalf_of and options, which are the run's. POST /api/v1/runs/:id/citations: { citations } (1-50, each with a source_type; the requester only). POST /api/v1/runs/:id/cancel and /retry take no body.
+ */
+export type AiRunCreateRequest =
+ | AiRunRequest
+ | {
+ /**
+ * @minItems 1
+ * @maxItems 50
+ */
+ citations: [
+ {
+ source_type: string;
+ source_id?: string;
+ url?: string;
+ title?: string;
+ author?: string;
+ snippet?: string;
+ provenance?: {};
+ },
+ ...{
+ source_type: string;
+ source_id?: string;
+ url?: string;
+ title?: string;
+ author?: string;
+ snippet?: string;
+ provenance?: {};
+ }[]
+ ];
+ }
+ | {
+ idempotency_key?: string;
+ target?: unknown;
+ attribution?: unknown;
+ on_behalf_of?: unknown;
+ options?: {};
+ }
+ | NoBody;
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.run-create-result@1: answers of ai.run.create on OpenVibe.AI. POST /api/v1/runs and the direct operations → { run, replayed? } (201 when it finished or was served from cache within the wait, 202 with Location while queued or running, 200 with replayed true for an idempotent repeat); POST …/cancel → { run }; POST …/retry → { run } (a new run with retry_of); POST …/citations → 201 { citations }. A quota refusal or a full queue is 429 with Retry-After.
+ */
+export type AiRunCreateResult =
+ | {
+ run: AiRun;
+ replayed?: true;
+ }
+ | {
+ citations: AiRunCitation[];
+ };
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.run-read-result@1: answers of ai.run.read on OpenVibe.AI (the caller's own runs; usage readers may ask for all). GET /api/v1/runs?status=&workflow=&target=&limit=&before= → { runs }; GET /api/v1/runs/:id → { run, citations, requests } (the request log is metadata only: hashes, tokens, cost, latency, never prompts); GET /api/v1/runs/:id/citations → { citations }. Someone else's run is 404 run.not_found.
+ */
+export type AiRunReadResult =
+ | {
+ runs: AiRun[];
+ }
+ | {
+ run: AiRun;
+ citations: AiRunCitation[];
+ requests: {
+ id: number;
+ seq: number;
+ operation?: string | null;
+ provider_key?: string | null;
+ model_key?: string | null;
+ status: string;
+ fallback?: unknown;
+ tokens_in?: number | null;
+ tokens_out?: number | null;
+ cost_usd?: number | null;
+ latency_ms?: number | null;
+ }[];
+ }
+ | {
+ citations: AiRunCitation[];
+ };
+
/** [email protected] (owner: tips) */
/** [email protected] (owner: tips) */
/**
/**
* tips.interaction.ready v1 (OpenVibe.Tips server/domain/interactions.js settle, summary()). The interaction settled: it is active and its effects (overlay alert, chat line, paid message, TTS, media request) are queued. Envelope: subject { type: interaction, id: tint_… }, visibility internal, priority important, actor service:tips. Never emitted for tests or imports.
* tips.interaction.ready v1 (OpenVibe.Tips server/domain/interactions.js settle, summary()). The interaction settled: it is active and its effects (overlay alert, chat line, paid message, TTS, media request) are queued. Envelope: subject { type: interaction, id: tint_… }, visibility internal, priority important, actor service:tips. Never emitted for tests or imports.
@@ -23551,6 +25397,16 @@ export interface NetworkStaffListResult {
@@ -23551,6 +25397,16 @@ export interface NetworkStaffListResult {
}[];
}[];
}
}
+/** [email protected] (owner: network) */
+/**
+ * network.module-write-request@1: the request of network.modules.write on OpenVibe.Network. PUT /internal/modules/:namespace/:subject: { data } — the record's new value, validated against the namespace's schema; If-Match names the revision read (412 if it moved). DELETE /internal/modules/:namespace/:subject (204) takes no body. The answer to PUT is modules.module-record@1.
+ */
+export type NetworkModuleWriteRequest =
+ | {
+ data: {};
+ }
+ | NoBody;
+
/** [email protected] (owner: tools) */
/** [email protected] (owner: tools) */
/**
/**
* One tool on openvibe.tools as its registry describes it (GET /api/v1/tools/:id, and each item of GET /api/v1/tools; capability tools.tool.read; ADR-027). There is one descriptor per catalogue tool (GET /api/catalog.json tools[].id), built from the tool's own code, so its page, the run API, openvibe-sdk/tools, the OpenAPI document and the docs all read the same facts. EXECUTION is where the tool's engine runs for its page: client (in the browser; nothing leaves it), sync (a server request answered inline) or job (an asynchronous job on a satellite, tools.job@1). API says whether POST /api/v1/tools/{id}/run exposes the tool. A client tool has api true only when it also has a server engine (a pure transform that runs in Node), and its page stays browser-only. A page-only tool (yt) has api false and run null. A job tool's run creates a job of run.job.type whose input is { ...input, ...run.job.preset, tool: run.job.operation } (the preset and the operation always win). INPUT is the JSON Schema (2020-12) that a run request's input must match, embedded or as { $ref: https://openvibe.tools/api/v1/tools/{id}/schema#/$defs/input }. GET /api/v1/tools/:id/schema answers { $schema, $id, $defs: { input, output } }, and the list uses the $ref form. When api is false, $defs.input is false (the schema that accepts nothing), since there is no run to take an input. OUTPUT.SCHEMA describes result.data for every execution: inline and job tools alike. KEYWORDS (the catalogue's search terms) and EXAMPLES (sample runs for the docs and the OpenAPI document) are optional. NOT A TOOL: there is no planned status. A planned catalogue entry has no descriptor and is not listed, and neither is a mirror (a second build of another tool). GET /api/v1/tools/:id and /schema answer 404 tools.tool.not_found for either, with a detail that says the id is planned or names the tool it mirrors, as for an id that does not exist. RULES this schema enforces: api false has no run and no examples, and api true has a run and an input; an API job tool names its job, and only job tools name one; an API tool whose output is a file runs as a job (its results are served as job files); a tools.net.probe tool fetches (egress) and is never anonymous; an anonymous egress tool has a per-target throttle (limits.perTargetPerMinute); a client tool never fetches; an unavailable tool says why (statusReason); JSON output has a schema. contracts.tools.checkDescriptor(d) also checks what depends on the id (run.path, $ref targets), files.min <= files.max, and each example: its input against an embedded input schema and limits.maxInputBytes, and its files against files (count and accept).
* One tool on openvibe.tools as its registry describes it (GET /api/v1/tools/:id, and each item of GET /api/v1/tools; capability tools.tool.read; ADR-027). There is one descriptor per catalogue tool (GET /api/catalog.json tools[].id), built from the tool's own code, so its page, the run API, openvibe-sdk/tools, the OpenAPI document and the docs all read the same facts. EXECUTION is where the tool's engine runs for its page: client (in the browser; nothing leaves it), sync (a server request answered inline) or job (an asynchronous job on a satellite, tools.job@1). API says whether POST /api/v1/tools/{id}/run exposes the tool. A client tool has api true only when it also has a server engine (a pure transform that runs in Node), and its page stays browser-only. A page-only tool (yt) has api false and run null. A job tool's run creates a job of run.job.type whose input is { ...input, ...run.job.preset, tool: run.job.operation } (the preset and the operation always win). INPUT is the JSON Schema (2020-12) that a run request's input must match, embedded or as { $ref: https://openvibe.tools/api/v1/tools/{id}/schema#/$defs/input }. GET /api/v1/tools/:id/schema answers { $schema, $id, $defs: { input, output } }, and the list uses the $ref form. When api is false, $defs.input is false (the schema that accepts nothing), since there is no run to take an input. OUTPUT.SCHEMA describes result.data for every execution: inline and job tools alike. KEYWORDS (the catalogue's search terms) and EXAMPLES (sample runs for the docs and the OpenAPI document) are optional. NOT A TOOL: there is no planned status. A planned catalogue entry has no descriptor and is not listed, and neither is a mirror (a second build of another tool). GET /api/v1/tools/:id and /schema answer 404 tools.tool.not_found for either, with a detail that says the id is planned or names the tool it mirrors, as for an id that does not exist. RULES this schema enforces: api false has no run and no examples, and api true has a run and an input; an API job tool names its job, and only job tools name one; an API tool whose output is a file runs as a job (its results are served as job files); a tools.net.probe tool fetches (egress) and is never anonymous; an anonymous egress tool has a per-target throttle (limits.perTargetPerMinute); a client tool never fetches; an unavailable tool says why (statusReason); JSON output has a schema. contracts.tools.checkDescriptor(d) also checks what depends on the id (run.path, $ref targets), files.min <= files.max, and each example: its input against an embedded input schema and limits.maxInputBytes, and its files against files (count and accept).
@@ -24040,6 +25896,34 @@ export interface ToolsJobRequest {
@@ -24040,6 +25896,34 @@ export interface ToolsJobRequest {
idempotency_key?: string;
idempotency_key?: string;
}
}
+/** [email protected] (owner: tools) */
+/**
+ * tools.job-create-request@1: the request of tools.job.create on the Tools satellites that run jobs. POST /api/v1/jobs: tools.job-request@1 (JSON or multipart). POST /api/v1/jobs/:id/retry and PUT|DELETE /api/v1/jobs/:id/references/:ref take no body.
+ */
+export type ToolsJobCreateRequest = ToolsJobRequest | NoBody;
+
+/** [email protected] (owner: tools) */
+/**
+ * tools.job-read-result@1: answers of tools.job.read on the Tools satellites. GET /api/v1/jobs/:id → tools.job@1, and so is the data of every event on GET /api/v1/jobs/:id/events (Server-Sent Events, Last-Event-ID resume); GET /api/v1/jobs/:id/files/:n → the result file's bytes (Content-Type of the file; 409 tools.job.not_ready before the job succeeded).
+ */
+export type ToolsJobReadResult = ToolsJob | string;
+
+/** [email protected] (owner: tools) */
+/**
+ * tools.tool-read-result@1: answers of tools.tool.read on openvibe.tools (public registry; ADR-027). GET /api/v1/tools → tools.tool-list@1; GET /api/v1/tools/:id → tools.tool@1 with its schemas embedded; GET /api/v1/tools/:id/schema → the tool's own JSON Schema document { $schema, $id, $defs: { input, output } } (application/schema+json; input is false for a tool without an API input).
+ */
+export type ToolsToolReadResult =
+ | ToolList
+ | ToolDescriptor
+ | {
+ $schema: string;
+ $id: string;
+ $defs: {
+ input: {} | boolean;
+ output: {};
+ };
+ };
+
/** [email protected] (owner: news) */
/** [email protected] (owner: news) */
/**
/**
* news.source.ingested v1 (OpenVibe.News server/domain/ingest.js ingestedEvent, from apply and applyRemoval). An OpenVibe.Sources item of category news changed what News holds, in the transaction that applied it: created (a new news_source_items row, with its dedupe outcome and the cluster it joined; news.cluster.updated follows in the same transaction), updated (Sources sent a newer revision of an item News has; adds previous_revision) or removed (Sources removed the item: sticky, the stored summary is dropped; adds reason and origin). Items arrive through the Events webhook (sources.item.*) or the cursor pull; a replay, an older or equal revision, an item of another category, an update to a removed item and a removal of an item never ingested emit nothing, and an item without a title is news.source.failed (state rejected) instead. Deliberately left out: the headline, outlet, authors, published_at and licensed summary (GET /api/v1/source-items/:id with news.cluster.read) and the cluster_reason explanation. Envelope: subject { type: source_item, id: nsi_… }, visibility internal, priority low, actor service:news.
* news.source.ingested v1 (OpenVibe.News server/domain/ingest.js ingestedEvent, from apply and applyRemoval). An OpenVibe.Sources item of category news changed what News holds, in the transaction that applied it: created (a new news_source_items row, with its dedupe outcome and the cluster it joined; news.cluster.updated follows in the same transaction), updated (Sources sent a newer revision of an item News has; adds previous_revision) or removed (Sources removed the item: sticky, the stored summary is dropped; adds reason and origin). Items arrive through the Events webhook (sources.item.*) or the cursor pull; a replay, an older or equal revision, an item of another category, an update to a removed item and a removal of an item never ingested emit nothing, and an item without a title is news.source.failed (state rejected) instead. Deliberately left out: the headline, outlet, authors, published_at and licensed summary (GET /api/v1/source-items/:id with news.cluster.read) and the cluster_reason explanation. Envelope: subject { type: source_item, id: nsi_… }, visibility internal, priority low, actor service:news.
@@ -36623,6 +38507,30 @@ export interface MediaObjectVisibilityChangedPayload {
@@ -36623,6 +38507,30 @@ export interface MediaObjectVisibilityChangedPayload {
changed_at: string;
changed_at: string;
}
}
+/** [email protected] (owner: media) */
+/**
+ * media.file-read-result@1: answers of media.object.read on OpenVibe.Media. GET /api/v1/:app/files/:key → media.file@1 (the file's metadata; 404 when it is not the tenant's); GET /f/:key → the bytes, with Content-Type and Range (a sandbox file only through its signed URL).
+ */
+export type MediaFileReadResult = MediaFile | string;
+
+/** [email protected] (owner: media) */
+/**
+ * media.file@1: one stored file of a tenant on OpenVibe.Media (server/files/routes.js filePublic): its key, owner, name, size, type and hash, and the URL it is served from (/f/<key>; a developer-project sandbox file gets a short-lived signed URL instead).
+ */
+export interface MediaFile {
+ key: string;
+ app_id: string;
+ user_id?: string | number | null;
+ original_name: string;
+ size: number;
+ mime: string;
+ sha256?: string | null;
+ url: string;
+ sandbox?: true;
+ url_expires_at?: string | number;
+ created_at: string;
+}
+
/** [email protected] (owner: community) */
/** [email protected] (owner: community) */
/**
/**
* community.paste.created v1 (OpenVibe.Community server/events.js, queued in the transaction of the change). A paste (text or screenshot) was created. Never the content or title. Envelope: subject { type: paste, id }, visibility public when the item is public else internal, priority low, actor the person or service:community.
* community.paste.created v1 (OpenVibe.Community server/events.js, queued in the transaction of the change). A paste (text or screenshot) was created. Never the content or title. Envelope: subject { type: paste, id }, visibility public when the item is public else internal, priority low, actor the person or service:community.
@@ -37695,26 +39603,7 @@ export type CommunityPostWriteRequest =
@@ -37695,26 +39603,7 @@ export type CommunityPostWriteRequest =
export type CommunityPulseWriteResult =
export type CommunityPulseWriteResult =
| {
| {
created: boolean;
created: boolean;
- item: {
- id: number;
- source: {
- service: string;
- type: string;
- id: string;
- };
- title: string;
- url: string;
- origin: "user" | "ai" | "system";
- label: "AI" | "System" | null;
- actor: {
- subject?: string | null;
- username?: string | null;
- display_name?: string | null;
- avatar_url?: string | null;
- profile_color?: string | null;
- } | null;
- occurred_at: string | null;
- };
+ item: CommunityPulseItem;
}
}
| {
| {
removed: number;
removed: number;
@@ -38072,6 +39961,239 @@ export type CommunityPulseWriteRequest =
@@ -38072,6 +39961,239 @@ export type CommunityPulseWriteRequest =
}
}
| NoBody;
| NoBody;
+/** [email protected] (owner: community) */
+/**
+ * community.comment-thread@1: one embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeThread): the entity it belongs to, its visibility and comment count. id is the sequential id for services and the access id (cth_…) for everyone else; a hidden thread shows moderators its count, others only that it is hidden.
+ */
+export interface CommunityCommentThread {
+ id: number | string;
+ access_id: string;
+ ref: EntityRef;
+ visibility: "public" | "hidden" | "locked";
+ comment_count: number | null;
+ created_at?: string | null;
+ updated_at?: string | null;
+}
+
+/** [email protected] (owner: community) */
+/**
+ * community.comment-write-request@1: bodies of community.comment.write on OpenVibe.Community (people, anonymous people with a name where the thread allows it, and services acting as X-OV-Subject or as AI). POST /api/v1/comments/threads/resolve: { ref } — get or create the thread of an entity (browsers only for the allow-listed entity types; a label is taken only from a service). POST /api/v1/comments/threads/:id/comments: { message, parent_id?, anon_name? } (message trimmed, at most 5000 characters; a reply to a reply joins the top-level comment). POST /api/v1/comments/:commentId/votes: { value: 1 | -1 | 0 } (people). GET /api/v1/comments/threads/:id and DELETE /api/v1/comments/:commentId take no body. Unknown fields are ignored.
+ */
+export type CommunityCommentWriteRequest =
+ | {
+ ref: EntityRef;
+ }
+ | {
+ message: string;
+ parent_id?: number | string | null;
+ anon_name?: string | null;
+ }
+ | {
+ value: 1 | -1 | 0 | "1" | "-1" | "0";
+ }
+ | NoBody;
+
+/** [email protected] (owner: community) */
+/**
+ * community.comment-write-result@1: answers of community.comment.write on OpenVibe.Community. POST …/threads/resolve → { thread, created } (201 when created); GET …/threads/:id?after=&sort=&limit=&parent= → { thread, comments, next_cursor, viewer }; POST …/threads/:id/comments → 201 { comment }; DELETE /api/v1/comments/:commentId → { ok, id }; POST …/:commentId/votes → { comment_id, score, upvotes, downvotes, my_vote }. Refusals are problem+json (thread.locked, auth.required, community.blocked, …).
+ */
+export type CommunityCommentWriteResult =
+ | {
+ thread: CommunityCommentThread;
+ created: boolean;
+ }
+ | {
+ thread: CommunityCommentThread;
+ comments: CommunityComment[];
+ next_cursor: string | null;
+ viewer: {
+ signed_in?: boolean;
+ can_comment?: boolean;
+ can_vote?: boolean;
+ can_moderate?: boolean;
+ };
+ }
+ | {
+ comment: CommunityComment;
+ }
+ | {
+ ok: true;
+ id: number;
+ }
+ | {
+ comment_id: number;
+ score: number;
+ upvotes: number;
+ downvotes: number;
+ my_vote: 1 | -1 | 0;
+ };
+
+/** [email protected] (owner: community) */
+/**
+ * community.comment@1: one comment in an embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeComment), with its author (or anonymous name), score and the caller's vote, reply count and, for top-level comments in a page, the first replies. A deleted comment keeps its place with no author or message.
+ */
+export interface CommunityComment {
+ id: number;
+ thread_id: number | string;
+ parent_id: number | null;
+ origin: string;
+ author: {
+ subject?: string | null;
+ username?: string | null;
+ display_name?: string | null;
+ avatar_url?: string | null;
+ profile_color?: string | null;
+ is_ai?: true;
+ is_system?: true;
+ } | null;
+ anon_name?: string | null;
+ display_name?: string | null;
+ message: string | null;
+ deleted: boolean;
+ score: number;
+ upvotes?: number;
+ downvotes?: number;
+ my_vote: 1 | -1 | 0;
+ reply_count?: number | null;
+ created_at?: string | null;
+ updated_at?: string | null;
+ edited_at?: string | null;
+ can_edit: boolean;
+ can_delete: boolean;
+ replies?: {}[];
+}
+
+/** [email protected] (owner: community) */
+/**
+ * community.forum-space@1: one forum space as OpenVibe.Community's /api/v1/spaces API shows it (server/forum/service.js shapeSpace): its slug, name, visibility, thread kind and statuses, style, whether votes and reactions are on, its group and parent board, the members-only gate and, in lists, its counts and last activity.
+ */
+export interface CommunityForumSpace {
+ slug: string;
+ name: string;
+ description?: string | null;
+ visibility: "public" | "members" | "staff";
+ url: string;
+ thread_kind: string;
+ statuses: string[];
+ style: "feed" | "forum";
+ votes: boolean;
+ reactions: boolean;
+ group: {
+ slug?: string;
+ name?: string;
+ } | null;
+ parent: {
+ slug?: string;
+ name?: string;
+ } | null;
+ post_count?: number;
+ thread_count?: number;
+ last_activity_at?: string | null;
+ members_only: {
+ owner?: string;
+ owner_username?: string | null;
+ join_url?: string | null;
+ } | null;
+ last_post?: {} | null;
+ children?: unknown[];
+}
+
+/** [email protected] (owner: community) */
+/**
+ * community.pulse-item@1: one item of the network's public activity on OpenVibe.Community (server/pulse/service.js shape), with provenance: its source entity, title, url, origin (user, ai or system), the AI or System label, the actor (a person for user items, never for AI or system ones) and when it happened.
+ */
+export interface CommunityPulseItem {
+ id: number;
+ source: {
+ service: string;
+ type: string;
+ id: string;
+ };
+ title: string;
+ url: string;
+ origin: "user" | "ai" | "system";
+ label: "AI" | "System" | null;
+ actor: {
+ subject?: string | null;
+ username?: string | null;
+ display_name?: string | null;
+ avatar_url?: string | null;
+ profile_color?: string | null;
+ is_ai?: true;
+ is_system?: true;
+ } | null;
+ occurred_at: string | null;
+}
+
+/** [email protected] (owner: community) */
+/**
+ * community.pulse-read-result@1: answers of community.pulse.read on OpenVibe.Community (anyone). GET /api/v1/pulse?origin=user|ai|system&after=&limit= → { items, next_cursor } (newest first, at most 100; an unknown origin or a cursor this API did not issue is 400). GET /pulse is the same activity as an HTML page.
+ */
+export type CommunityPulseReadResult =
+ | {
+ /**
+ * @maxItems 100
+ */
+ items: CommunityPulseItem[];
+ next_cursor: string | null;
+ }
+ | string;
+
+/** [email protected] (owner: community) */
+/**
+ * community.space-read-result@1: answers of community.space.read on OpenVibe.Community. GET /api/v1/spaces → { spaces, groups } (the spaces the caller can open, each with its last post, and the board index: groups in order with their top-level spaces and child boards); GET /api/v1/spaces/:space → { space } (a staff or members-only space the caller cannot open is 404 or 401/403 vip.members_only); GET /s is the board index as an HTML page.
+ */
+export type CommunitySpaceReadResult =
+ | {
+ spaces: CommunityForumSpace[];
+ groups: {
+ slug: string | null;
+ name: string;
+ description?: string | null;
+ spaces: unknown[];
+ }[];
+ }
+ | {
+ space: CommunityForumSpace;
+ }
+ | string;
+
+/** [email protected] (owner: community) */
+/**
+ * community.thread-read-result@1: answers of community.thread.read on OpenVibe.Community. GET /api/v1/spaces/:space/threads?sort=&page=&limit=&category=&status= → a page of threads with the space, paging, categories, children and what the viewer may do; GET /api/v1/spaces/:space/threads/:slug?page= → the thread with a page of posts (reactions, author stats, crosspost info, attachments settings, paging, viewer). GET /s/:space and /s/:space/t/:slug are the same as HTML pages; GET /s/feed.xml and /s/:space/feed.xml are RSS feeds of public threads.
+ */
+export type CommunityThreadReadResult =
+ | {
+ space: {};
+ sort?: string;
+ page: number;
+ per_page?: number;
+ total: number;
+ pages: number;
+ categories?: unknown[];
+ category?: string | null;
+ status?: string | null;
+ viewer?: {};
+ children?: unknown[];
+ groups?: unknown[];
+ threads: CommunityForumThread[];
+ }
+ | {
+ space: {};
+ thread: CommunityForumThread;
+ posts: CommunityForumPost[];
+ reactions?: unknown[];
+ crosspost?: {};
+ categories?: unknown[];
+ attachments?: {};
+ page: number;
+ per_page?: number;
+ pages: number;
+ total?: number;
+ viewer?: {};
+ }
+ | string;
+
/** [email protected] (owner: openre) */
/** [email protected] (owner: openre) */
/**
/**
* openre.destination@1: one restream destination of a stream on OpenRe.Stream (server/store/outputs.js publicDest). The stream key and SRT passphrase are write-only: only whether they are set and a hint of the key are shown.
* openre.destination@1: one restream destination of a stream on OpenRe.Stream (server/store/outputs.js publicDest). The stream key and SRT passphrase are write-only: only whether they are set and a hint of the key are shown.
@@ -5,8 +5,8 @@
@@ -5,8 +5,8 @@
"status": "active",
"status": "active",
"visibility": "first-party",
"visibility": "first-party",
"description": "Create workflow runs (POST /api/v1/runs and the direct chat/generate/summarize/classify/extract/enrich/embed operations), cancel or retry the caller's own runs and attach citations to them. A token's `ns` claim limits which workflow namespaces it may run (live.*, wiki.*, ...). Quotas are checked before any provider call.",
"description": "Create workflow runs (POST /api/v1/runs and the direct chat/generate/summarize/classify/extract/enrich/embed operations), cancel or retry the caller's own runs and attach citations to them. A token's `ns` claim limits which workflow namespaces it may run (live.*, wiki.*, ...). Quotas are checked before any provider call.",
- "inputSchema": "ai.run-request@1",
- "outputSchema": "ai.run@1",
+ "inputSchema": "ai.run-create-request@1",
+ "outputSchema": "ai.run-create-result@1",
"permissions": [
"permissions": [
"ai:run"
"ai:run"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "first-party",
"visibility": "first-party",
"description": "Read the caller's own runs, their citations and request-log metadata (hashes, tokens, latency; never raw prompts).",
"description": "Read the caller's own runs, their citations and request-log metadata (hashes, tokens, latency; never raw prompts).",
- "outputSchema": "ai.run@1",
+ "outputSchema": "ai.run-read-result@1",
"permissions": [
"permissions": [
"ai:read"
"ai:read"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "public",
"visibility": "public",
"description": "An app manages release metadata for ITSELF with its own token (audience openvibe.codes): create a release from a validated app or mod manifest, publish, deprecate (with a reason and an optional replacement) and revoke it. The token's sub must be app:<the app in the path>; people manage releases in the portal with their project role instead. Trust tiers are not part of this capability (staff metadata only).",
"description": "An app manages release metadata for ITSELF with its own token (audience openvibe.codes): create a release from a validated app or mod manifest, publish, deprecate (with a reason and an optional replacement) and revoke it. The token's sub must be app:<the app in the path>; people manage releases in the portal with their project role instead. Trust tiers are not part of this capability (staff metadata only).",
- "inputSchema": "codes.app-manifest@1",
+ "inputSchema": "codes.release-manage-request@1",
"permissions": ["codes:releases:write"],
"permissions": ["codes:releases:write"],
"resourceConstraints": ["owner", "project"],
"resourceConstraints": ["owner", "project"],
"quotaClass": "codes-release-write",
"quotaClass": "codes-release-write",
@@ -5,8 +5,8 @@
@@ -5,8 +5,8 @@
"status": "active",
"status": "active",
"visibility": "first-party",
"visibility": "first-party",
"description": "Typed comment threads as a service: resolve the thread of any EntityRef (browsers are limited to an allowlist of types), and comment, vote and delete one's own comments as the acting subject (X-OV-Subject), or comment as origin \"ai\" (never attributed to a person, roadmap 33). Ownership and limits are checked by Community.",
"description": "Typed comment threads as a service: resolve the thread of any EntityRef (browsers are limited to an allowlist of types), and comment, vote and delete one's own comments as the acting subject (X-OV-Subject), or comment as origin \"ai\" (never attributed to a person, roadmap 33). Ownership and limits are checked by Community.",
- "inputSchema": "common.entity-ref@1",
- "outputSchema": "common.entity-ref@1",
+ "inputSchema": "community.comment-write-request@1",
+ "outputSchema": "community.comment-write-result@1",
"permissions": [
"permissions": [
"community:write"
"community:write"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "public",
"visibility": "public",
"description": "Read the network's public activity feed (Pulse): entity reference, title, URL and origin (user, ai or system), newest first with a cursor. Only public items are ever stored. Served to anyone (CORS allow-list for browsers). Live's /api/home/pulse is a different, Live-only feed.",
"description": "Read the network's public activity feed (Pulse): entity reference, title, URL and origin (user, ai or system), newest first with a cursor. Only public items are ever stored. Served to anyone (CORS allow-list for browsers). Live's /api/home/pulse is a different, Live-only feed.",
- "outputSchema": "common.entity-ref@1",
+ "outputSchema": "community.pulse-read-result@1",
"permissions": [
"permissions": [
"community:read"
"community:read"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "public",
"visibility": "public",
"description": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Community checks space visibility itself.",
"description": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Community checks space visibility itself.",
- "outputSchema": "common.entity-ref@1",
+ "outputSchema": "community.space-read-result@1",
"permissions": [
"permissions": [
"community:read"
"community:read"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "public",
"visibility": "public",
"description": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds. Community checks space visibility and the VIP gate (403 vip.members_only). Creating threads and replies stays community.post.create.",
"description": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds. Community checks space visibility and the VIP gate (403 vip.members_only). Creating threads and replies stays community.post.create.",
- "outputSchema": "common.entity-ref@1",
+ "outputSchema": "community.thread-read-result@1",
"permissions": [
"permissions": [
"community:read"
"community:read"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "public",
"visibility": "public",
"description": "Publish durable events (events.event-envelope@1) as a developer app (ADR-014). The app token (sub app:app_<ULID>, project_id prj_<ULID>, env) may publish only event types app.<project_key>.<name>[.<more>], where project_key is 'p' followed by the project's ULID in lowercase (prj_01JAB... -> p01jab...). source must be 'app-' followed by the app's ULID in lowercase; actor is the app (or the on_behalf_of user). Events stores the token's env with each event: sandbox events are never delivered to production subscriptions nor shown to production apps. Rate and retained bytes are limited per project and environment by OpenVibe.Events.",
"description": "Publish durable events (events.event-envelope@1) as a developer app (ADR-014). The app token (sub app:app_<ULID>, project_id prj_<ULID>, env) may publish only event types app.<project_key>.<name>[.<more>], where project_key is 'p' followed by the project's ULID in lowercase (prj_01JAB... -> p01jab...). source must be 'app-' followed by the app's ULID in lowercase; actor is the app (or the on_behalf_of user). Events stores the token's env with each event: sandbox events are never delivered to production subscriptions nor shown to production apps. Rate and retained bytes are limited per project and environment by OpenVibe.Events.",
- "inputSchema": "events.event-envelope@1",
+ "inputSchema": "events.publish-request@1",
"permissions": [
"permissions": [
"events:write"
"events:write"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "internal",
"visibility": "internal",
"description": "Publish durable events (events.event-envelope@1) as the calling service: source must be the principal's service slug and event_type must use a prefix that source owns. Idempotent on event_id.",
"description": "Publish durable events (events.event-envelope@1) as the calling service: source must be the principal's service slug and event_type must use a prefix that source owns. Idempotent on event_id.",
- "inputSchema": "events.event-envelope@1",
+ "inputSchema": "events.publish-request@1",
"permissions": [
"permissions": [
"events:write"
"events:write"
],
],
@@ -5,8 +5,8 @@
@@ -5,8 +5,8 @@
"status": "active",
"status": "active",
"visibility": "first-party",
"visibility": "first-party",
"description": "Resolve a subject (or a legacy service-local id) to its canonical SubjectRef and display projection.",
"description": "Resolve a subject (or a legacy service-local id) to its canonical SubjectRef and display projection.",
- "inputSchema": "identity.legacy-identity-map@1",
- "outputSchema": "identity.subject-ref@1",
+ "inputSchema": "identity.resolve-request@1",
+ "outputSchema": "identity.resolve-result@1",
"permissions": [
"permissions": [
"identity:read"
"identity:read"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "public",
"visibility": "public",
"description": "Read object metadata / bytes in a namespace.",
"description": "Read object metadata / bytes in a namespace.",
- "inputSchema": "media.media-ref@1",
+ "inputSchema": "common.no-body@1",
"permissions": [
"permissions": [
"media:read"
"media:read"
],
],
@@ -18,5 +18,5 @@
@@ -18,5 +18,5 @@
"GET /api/v1/:app/files/:key",
"GET /api/v1/:app/files/:key",
"GET /f/:key"
"GET /f/:key"
],
],
- "outputSchema": "common.binary@1"
+ "outputSchema": "media.file-read-result@1"
}
}
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "public",
"visibility": "public",
"description": "Upload a file object into an app namespace.",
"description": "Upload a file object into an app namespace.",
- "outputSchema": "media.media-ref@1",
+ "outputSchema": "media.file@1",
"permissions": [
"permissions": [
"media:write"
"media:write"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "first-party",
"visibility": "first-party",
"description": "Write a subject's record in a namespace the service owns (If-Match revision).",
"description": "Write a subject's record in a namespace the service owns (If-Match revision).",
- "inputSchema": "modules.module-record@1",
+ "inputSchema": "network.module-write-request@1",
"outputSchema": "modules.module-record@1",
"outputSchema": "modules.module-record@1",
"permissions": [
"permissions": [
"modules:write"
"modules:write"
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "first-party",
"visibility": "first-party",
"description": "Index, replace or tombstone the calling service's own documents (search.index-document@1) and read its own inventory for reconciliation. The owner in the path must be the principal's service slug (svc:wiki writes owner wiki only). Revision-ordered: an older revision never overwrites a newer one, and at equal revision a deletion wins.",
"description": "Index, replace or tombstone the calling service's own documents (search.index-document@1) and read its own inventory for reconciliation. The owner in the path must be the principal's service slug (svc:wiki writes owner wiki only). Revision-ordered: an older revision never overwrites a newer one, and at equal revision a deletion wins.",
- "inputSchema": "search.index-document@1",
+ "inputSchema": "search.document-write-request@1",
"permissions": [
"permissions": [
"search:write"
"search:write"
],
],
@@ -23,5 +23,5 @@
@@ -23,5 +23,5 @@
"GET /api/v1/owners/:owner/documents/:type/:id",
"GET /api/v1/owners/:owner/documents/:type/:id",
"GET /api/v1/owners/:owner/rejections"
"GET /api/v1/owners/:owner/rejections"
],
],
- "outputSchema": "search.write-result@1"
+ "outputSchema": "search.owner-result@1"
}
}
@@ -19,5 +19,5 @@
@@ -19,5 +19,5 @@
"GET /api/v1/documents/:owner/:type/:id"
"GET /api/v1/documents/:owner/:type/:id"
],
],
"inputSchema": "search.query@1",
"inputSchema": "search.query@1",
- "outputSchema": "search.query-result@1"
+ "outputSchema": "search.read-result@1"
}
}
@@ -19,5 +19,5 @@
@@ -19,5 +19,5 @@
"GET /api/v1/documents/:owner/:type/:id"
"GET /api/v1/documents/:owner/:type/:id"
],
],
"inputSchema": "search.query@1",
"inputSchema": "search.query@1",
- "outputSchema": "search.query-result@1"
+ "outputSchema": "search.read-result@1"
}
}
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "first-party",
"visibility": "first-party",
"description": "Read source items with provenance (source, canonical URL, retrieval time, content and raw-body hashes, parser version, terms) in change order, and one item with its revisions. Each page states the staleness of the sources it contains.",
"description": "Read source items with provenance (source, canonical URL, retrieval time, content and raw-body hashes, parser version, terms) in change order, and one item with its revisions. Each page states the staleness of the sources it contains.",
- "outputSchema": "sources.item@1",
+ "outputSchema": "sources.item-read-result@1",
"permissions": [
"permissions": [
"sources:read"
"sources:read"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "first-party",
"visibility": "first-party",
"description": "List registered sources with health and staleness, their fetch runs (every attempt with an explicit state) and the health summary. Credential values are never part of any response; only the variable name is.",
"description": "List registered sources with health and staleness, their fetch runs (every attempt with an explicit state) and the health summary. Credential values are never part of any response; only the variable name is.",
- "outputSchema": "sources.source@1",
+ "outputSchema": "sources.source-read-result@1",
"permissions": [
"permissions": [
"sources:read"
"sources:read"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "public",
"visibility": "public",
"description": "Submit an asynchronous tool job (img.process, audio.process, docs.process) with its input files (tools.job-request@1, JSON or multipart); the job is owned by the calling principal. Idempotency-Key supported. Also the owner's other writes on a job: retry a failed job as a new one (idempotent: asking again returns that retry), and keep a succeeded job's result while a reference such as community:paste:p_123 points at it (PUT), or let it expire again (DELETE, after the last reference).",
"description": "Submit an asynchronous tool job (img.process, audio.process, docs.process) with its input files (tools.job-request@1, JSON or multipart); the job is owned by the calling principal. Idempotency-Key supported. Also the owner's other writes on a job: retry a failed job as a new one (idempotent: asking again returns that retry), and keep a succeeded job's result while a reference such as community:paste:p_123 points at it (PUT), or let it expire again (DELETE, after the last reference).",
- "inputSchema": "tools.job-request@1",
+ "inputSchema": "tools.job-create-request@1",
"outputSchema": "tools.job@1",
"outputSchema": "tools.job@1",
"permissions": [
"permissions": [
"tools:jobs:write"
"tools:jobs:write"
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "public",
"visibility": "public",
"description": "Read a job the calling principal created: state, progress, result (Media object references or local files), SSE progress with Last-Event-ID resume, result files.",
"description": "Read a job the calling principal created: state, progress, result (Media object references or local files), SSE progress with Last-Event-ID resume, result files.",
- "outputSchema": "tools.job@1",
+ "outputSchema": "tools.job-read-result@1",
"permissions": [
"permissions": [
"tools:jobs:read"
"tools:jobs:read"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "public",
"visibility": "public",
"description": "Read the tool registry (ADR-027): every tool's descriptor (tools.tool@1: execution, whether the run API exposes it, how to call it, input and output schemas, files, limits, auth, quota class, cost, egress, hosts, docs), filtered by family, execution, api, status or text. GET /api/v1/tools answers tools.tool-list@1 with schemas as { $ref }; GET /api/v1/tools/:id has them embedded; GET /api/v1/tools/:id/schema answers { $schema, $id, $defs: { input, output } }, with $defs.input false for a tool without an API. The gateway (https://openvibe.tools) answers for every tool, and each satellite for its own tools with its own live status. Open to everyone, anonymous callers included, from any origin (CORS *); cacheable (ETag, 304). A bad filter value is 400 tools.query.invalid; an unknown, planned or mirror id is 404 tools.tool.not_found, whose detail says which.",
"description": "Read the tool registry (ADR-027): every tool's descriptor (tools.tool@1: execution, whether the run API exposes it, how to call it, input and output schemas, files, limits, auth, quota class, cost, egress, hosts, docs), filtered by family, execution, api, status or text. GET /api/v1/tools answers tools.tool-list@1 with schemas as { $ref }; GET /api/v1/tools/:id has them embedded; GET /api/v1/tools/:id/schema answers { $schema, $id, $defs: { input, output } }, with $defs.input false for a tool without an API. The gateway (https://openvibe.tools) answers for every tool, and each satellite for its own tools with its own live status. Open to everyone, anonymous callers included, from any origin (CORS *); cacheable (ETag, 304). A bad filter value is 400 tools.query.invalid; an unknown, planned or mirror id is 404 tools.tool.not_found, whose detail says which.",
- "outputSchema": "tools.tool@1",
+ "outputSchema": "tools.tool-read-result@1",
"permissions": [
"permissions": [
"tools:catalog:read"
"tools:catalog:read"
],
],
@@ -229,12 +229,12 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
@@ -229,12 +229,12 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
ok(c && c.owner === 'tools' && c.inputSchema === 'tools.run-request@1' && c.outputSchema === 'tools.run@1' && c.implementedBy.some(r => r.startsWith('POST /api/v1/tools/:id/run')), `${id} is the run API`);
ok(c && c.owner === 'tools' && c.inputSchema === 'tools.run-request@1' && c.outputSchema === 'tools.run@1' && c.implementedBy.some(r => r.startsWith('POST /api/v1/tools/:id/run')), `${id} is the run API`);
}
}
const [readCap, runCap, probeCap] = ['tools.tool.read', 'tools.tool.run', 'tools.net.probe'].map(id => capabilities.get(id));
const [readCap, runCap, probeCap] = ['tools.tool.read', 'tools.tool.run', 'tools.net.probe'].map(id => capabilities.get(id));
- ok(readCap.visibility === 'public' && readCap.quotaClass === 'tools-read' && readCap.outputSchema === 'tools.tool@1' && ['GET /api/v1/tools', 'GET /api/v1/tools/:id', 'GET /api/v1/tools/:id/schema'].every(r => readCap.implementedBy.includes(r)), 'tools.tool.read: public registry routes');
+ ok(readCap.visibility === 'public' && readCap.quotaClass === 'tools-read' && readCap.outputSchema === 'tools.tool-read-result@1' && JSON.stringify(contracts.schema('tools.tool-read-result').anyOf.slice(0, 2)) === JSON.stringify([{ $ref: 'tool-list.v1.json' }, { $ref: 'tool.v1.json' }]) && ['GET /api/v1/tools', 'GET /api/v1/tools/:id', 'GET /api/v1/tools/:id/schema'].every(r => readCap.implementedBy.includes(r)), 'tools.tool.read: public registry routes');
ok(runCap.visibility === 'public' && runCap.quotaClass === 'tools-run', 'tools.tool.run is public');
ok(runCap.visibility === 'public' && runCap.quotaClass === 'tools-run', 'tools.tool.run is public');
ok(probeCap.visibility === 'partner' && probeCap.quotaClass === 'tools-probe', 'tools.net.probe is partner: staff-set allowances only, never a default one');
ok(probeCap.visibility === 'partner' && probeCap.quotaClass === 'tools-probe', 'tools.net.probe is partner: staff-set allowances only, never a default one');
const jobCreate = capabilities.get('tools.job.create');
const jobCreate = capabilities.get('tools.job.create');
- ok(['POST /api/v1/jobs/:id/retry', 'PUT|DELETE /api/v1/jobs/:id/references/:ref'].every(r => jobCreate.implementedBy.includes(r)) && jobCreate.inputSchema === 'tools.job-request@1', 'tools.job.create covers submit, retry and references');
- ok(['tools.job.create', 'tools.job.read', 'tools.job.cancel'].every(id => capabilities.get(id).outputSchema === 'tools.job@1'), 'the job capabilities answer tools.job@1');
+ ok(['POST /api/v1/jobs/:id/retry', 'PUT|DELETE /api/v1/jobs/:id/references/:ref'].every(r => jobCreate.implementedBy.includes(r)) && jobCreate.inputSchema === 'tools.job-create-request@1' && contracts.schema('tools.job-create-request').anyOf[0].$ref === 'job-request.v1.json', 'tools.job.create covers submit (tools.job-request@1), retry and references');
+ ok(['tools.job.create', 'tools.job.cancel'].every(id => capabilities.get(id).outputSchema === 'tools.job@1') && capabilities.get('tools.job.read').outputSchema === 'tools.job-read-result@1' && contracts.schema('tools.job-read-result').anyOf[0].$ref === 'job.v1.json', 'the job capabilities answer tools.job@1 (reads also serve result files)');
const svc = services.get('tools');
const svc = services.get('tools');
ok(svc.ready === '/api/ready' && ['tools.tool.read', 'tools.tool.run', 'tools.net.probe'].every(c => svc.capabilities.includes(c)), 'the tools manifest lists the registry and run capabilities and its ready path');
ok(svc.ready === '/api/ready' && ['tools.tool.read', 'tools.tool.run', 'tools.net.probe'].every(c => svc.capabilities.includes(c)), 'the tools manifest lists the registry and run capabilities and its ready path');
for (const t of ['created', 'started', 'succeeded', 'failed']) ok(contracts.resolve(`tools.job.${t}`).status === 'active', `tools.job.${t} is emitted (active)`);
for (const t of ['created', 'started', 'succeeded', 'failed']) ok(contracts.resolve(`tools.job.${t}`).status === 'active', `tools.job.${t} is emitted (active)`);