openvibers/openvibe.contracts Commits
Contracts 0.82.0: Space and the media hub (plan T10, J2). manifests/services/space.json (openvibe.space) and media-hub.json (openvibe.video, openvibe.pics, openvibe.download), both placeholders until they serve; space.forum.read/manage, space.thread.read/write, space.post.write, space.pulse.read with seven space.* contracts and fixtures; planned video.*, pics.*, download.* capabilities. The community.space/thread/post capabilities and their forum contracts are deprecated with space.* replacements (removed after 0.84.0), still loadable and still Community's until Space serves. 14589 checks; compat vs v0.81.0: no breaking changes.
Changes
@@@ -4,6 +4,28 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) thfrom `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,the notes were in the tag and commit messages (`git tag -n1`).++## 0.82.0 — 2026-09-29++++**Space and the media hub: the forum moves off Community and the three new products register** (plan T10, D1–D4).++- Two service manifests, both `status: placeholder` / `exposure.state: placeholder` (the schema has no `planned`; a++ placeholder is "charter only", which is what these are until each serves): `manifests/services/space.json`++ (OpenVibe.Space, `openvibe.space`) and `manifests/services/media-hub.json` (OpenVibe.MediaHub). The media hub carries++ all three brand domains in one manifest (`domains: [openvibe.video, openvibe.pics, openvibe.download]`, the same list++ mechanism Network/Live/OpenRe/Games already use); the per-brand nav entry awaits the ADR-039 brand mechanism (T11).++- The forum capabilities are renamed off Community: `community.space.read` → `space.forum.read` (active),++ `community.space.manage` → `space.forum.manage` (planned), `community.thread.read` → `space.thread.read` (active),++ `community.post.create` → `space.post.write` (active), plus the new planned `space.thread.write` and++ `space.pulse.read`. The four old capability manifests stay loadable with `status: deprecated` and are still listed by++ `community` until their window closes.++- The seven forum contracts are re-owned under `space.*` (`space.forum-space/thread/post`, `space.forum-read-result`,++ `space.thread-read-result`, `space.post-write-request/result`) with fixtures; the `community.*` originals go++ `deprecated` in the catalog and get a `compatibility/deprecations.json` record naming the replacement and window++ (compat vs v0.80.0: no breaking changes). Contracts only exist for the three active capabilities.++- The smallest honest set for the media hub, all planned: `video.vod.read`, `video.playlist.read`,++ `video.playlist.manage`; `pics.image.read`, `pics.image.upload`, `pics.album.read`, `pics.album.manage`;++ `download.file.read`, `download.file.upload`, `download.share.create`. No media-hub capability is active, so none has++ a request/result contract yet.++## 0.81.0 — 2026-09-29**Opaque cursors beside the global seq** (plan T7, ADR-042, additive).@@@ -1 +1,54 @@--{ "_about": "Deprecated contracts: { id, replacement, since, removeAfter, reason }. A deprecated contract stays loadable until removeAfter.", "deprecations": [] }++{++ "_about": "Deprecated contracts: { id, replacement, since, removeAfter, reason }. A deprecated contract stays loadable until removeAfter.",++ "deprecations": [++ {++ "id": "community.forum-space",++ "replacement": "space.forum-space@1",++ "since": "0.82.0",++ "removeAfter": "0.84.0",++ "reason": "the forum, its spaces/threads/posts and the community.space.*/thread.read/post.create capabilities move to OpenVibe.Space (plan T10, D1/D3)"++ },++ {++ "id": "community.forum-thread",++ "replacement": "space.forum-thread@1",++ "since": "0.82.0",++ "removeAfter": "0.84.0",++ "reason": "the forum data moves to OpenVibe.Space (plan T10, D1/D3)"++ },++ {++ "id": "community.forum-post",++ "replacement": "space.forum-post@1",++ "since": "0.82.0",++ "removeAfter": "0.84.0",++ "reason": "the forum data moves to OpenVibe.Space (plan T10, D1/D3)"++ },++ {++ "id": "community.space-read-result",++ "replacement": "space.forum-read-result@1",++ "since": "0.82.0",++ "removeAfter": "0.84.0",++ "reason": "community.space.read is renamed space.forum.read (plan T10, D3)"++ },++ {++ "id": "community.thread-read-result",++ "replacement": "space.thread-read-result@1",++ "since": "0.82.0",++ "removeAfter": "0.84.0",++ "reason": "community.thread.read is renamed space.thread.read (plan T10, D3)"++ },++ {++ "id": "community.post-write-request",++ "replacement": "space.post-write-request@1",++ "since": "0.82.0",++ "removeAfter": "0.84.0",++ "reason": "community.post.create is renamed space.post.write (plan T10, D3)"++ },++ {++ "id": "community.post-write-result",++ "replacement": "space.post-write-result@1",++ "since": "0.82.0",++ "removeAfter": "0.84.0",++ "reason": "community.post.create is renamed space.post.write (plan T10, D3)"++ }++ ]++}@@@ -4700,7 +4700,7 @@"schema": "community/post-write-request.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "active"++ "status": "deprecated"},{"id": "community.pulse-write-result",@@@ -4718,7 +4718,7 @@"schema": "community/forum-post.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "active"++ "status": "deprecated"},{"id": "community.forum-thread",@@@ -4727,7 +4727,7 @@"schema": "community/forum-thread.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "active"++ "status": "deprecated"},{"id": "community.paste-create-result",@@@ -4745,7 +4745,7 @@"schema": "community/post-write-result.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "active"++ "status": "deprecated"},{"id": "community.pulse-write-request",@@@ -4799,7 +4799,7 @@"schema": "community/forum-space.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "active"++ "status": "deprecated"},{"id": "community.pulse-item",@@@ -4826,7 +4826,7 @@"schema": "community/space-read-result.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "active"++ "status": "deprecated"},{"id": "community.thread-read-result",@@@ -4835,6 +4835,69 @@"schema": "community/thread-read-result.v1.json","visibility": "first-party","compatibility": "backward",++ "status": "deprecated"++ },++ {++ "id": "space.forum-post",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/forum-post.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.forum-read-result",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/forum-read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.forum-space",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/forum-space.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.forum-thread",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/forum-thread.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.post-write-request",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/post-write-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.post-write-result",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/post-write-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.thread-read-result",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/thread-read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward","status": "active"},{nnew file mode 100644@@@ -0,0 +1,190 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/forum-post.v1.json",++ "title": "SpaceForumPost",++ "description": "space.forum-post@1: one forum post (the opening post of a thread or a reply) as OpenVibe.Space's API shows it (server/forum/service.js shapePost). A deleted post keeps its place with no author, body, attachments or pastes.",++ "type": "object",++ "required": [++ "id",++ "thread_id",++ "is_opening",++ "origin",++ "author",++ "body_markdown",++ "body_html",++ "revision",++ "deleted",++ "can_edit",++ "attachments",++ "pastes"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "thread_id": {++ "type": "integer"++ },++ "is_opening": {++ "type": "boolean"++ },++ "origin": {++ "type": "string"++ },++ "author": {++ "type": [++ "object",++ "null"++ ],++ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",++ "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++ }++ }++ },++ "body_markdown": {++ "type": [++ "string",++ "null"++ ]++ },++ "body_html": {++ "type": [++ "string",++ "null"++ ]++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "deleted": {++ "type": "boolean"++ },++ "created_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "can_edit": {++ "type": "boolean"++ },++ "attachments": {++ "type": "array",++ "maxItems": 4,++ "items": {++ "type": "object",++ "required": [++ "media_id",++ "url"++ ],++ "properties": {++ "media_id": {++ "type": "string"++ },++ "url": {++ "type": "string"++ },++ "filename": {++ "type": [++ "string",++ "null"++ ]++ },++ "mime": {++ "type": "string"++ },++ "size_bytes": {++ "type": "integer"++ }++ }++ }++ },++ "pastes": {++ "type": "array",++ "maxItems": 4,++ "items": {++ "type": "object",++ "required": [++ "slug",++ "url"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "title": {++ "type": "string"++ },++ "language": {++ "type": "string"++ },++ "type": {++ "type": "string"++ },++ "url": {++ "type": "string"++ },++ "screenshot_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "lines": {++ "type": "integer"++ },++ "excerpt": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,70 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/forum-read-result.v1.json",++ "title": "SpaceForumReadResult",++ "description": "space.forum-read-result@1: answers of space.forum.read on OpenVibe.Space. 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,139 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/forum-space.v1.json",++ "title": "SpaceForumSpace",++ "description": "space.forum-space@1: one forum space as OpenVibe.Space'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,247 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/forum-thread.v1.json",++ "title": "SpaceForumThread",++ "description": "space.forum-thread@1: one forum thread as OpenVibe.Space's /api/v1/spaces API shows it (server/forum/service.js shapeThread): where it lives, its kind and status, category, members-only gate, author, origin, pin and lock state, score, reply count, views, pages, the last post and the caller's vote.",++ "type": "object",++ "required": [++ "id",++ "space",++ "slug",++ "title",++ "url",++ "kind",++ "author",++ "origin",++ "pinned",++ "locked",++ "score",++ "reply_count",++ "pages",++ "created_at",++ "my_vote"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "space": {++ "type": "string"++ },++ "slug": {++ "type": "string"++ },++ "title": {++ "type": "string",++ "maxLength": 200++ },++ "url": {++ "type": "string",++ "description": "/s/<space>/t/<slug>"++ },++ "kind": {++ "type": "string",++ "description": "discussion, request, roadmap, …"++ },++ "status": {++ "type": [++ "string",++ "null"++ ]++ },++ "category": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "description": {++ "type": [++ "string",++ "null"++ ]++ },++ "position": {},++ "thread_count": {++ "type": "integer"++ }++ }++ },++ "members_only": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "owner": {++ "type": "string"++ },++ "owner_username": {++ "type": [++ "string",++ "null"++ ]++ },++ "join_url": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ },++ "author": {++ "type": [++ "object",++ "null"++ ],++ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",++ "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++ }++ }++ },++ "origin": {++ "type": "string"++ },++ "pinned": {++ "type": "boolean"++ },++ "locked": {++ "type": "boolean"++ },++ "score": {++ "type": "integer"++ },++ "reply_count": {++ "type": "integer",++ "minimum": 0++ },++ "views": {++ "type": "integer",++ "minimum": 0++ },++ "pages": {++ "type": "integer",++ "minimum": 1++ },++ "last_post": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "author": {++ "type": [++ "object",++ "null"++ ],++ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",++ "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++ }++ }++ }++ }++ },++ "crosspost_of": {},++ "last_activity_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "created_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "my_vote": {++ "enum": [++ 1,++ -1,++ 0++ ]++ }++ }++}nnew file mode 100644@@@ -0,0 +1,49 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/post-write-request.v1.json",++ "title": "SpacePostWriteRequest",++ "description": "space.post-write-request@1: bodies of the forum writes on OpenVibe.Space (space.post.write; a service writes as X-OV-Subject, or as AI with X-OV-Origin: ai). POST /api/v1/spaces/:space/threads: { title, body, members_only?, category?, attachments?, pastes? } (title 3 to 200 characters after whitespace is collapsed; body Markdown, non-blank, at most 40000 characters; body_markdown is accepted for body). POST …/threads/:slug/posts (a reply) and PUT /api/v1/posts/:id (an edit): { body, attachments?, pastes? }. POST …/threads/:slug/votes: { value: 1 | -1 | 0 }. DELETE /api/v1/posts/:id and DELETE /api/v1/spaces/:space/threads/:slug take no body. Unknown fields are ignored; refusals are problem+json (post.empty, post.too_long, thread.invalid_title, vote.invalid, thread.locked, vip.members_only, …).",++ "anyOf": [++ {++ "type": "object",++ "anyOf": [++ { "required": ["body"] },++ { "required": ["body_markdown"] }++ ],++ "properties": {++ "title": { "type": "string", "description": "A new thread's title." },++ "body": { "type": "string", "minLength": 1, "maxLength": 40000, "description": "Markdown." },++ "body_markdown": { "type": "string", "minLength": 1, "maxLength": 40000, "description": "Read when body is absent." },++ "members_only": {++ "description": "A new thread only: true gates it to the author's own VIP members; { owner: usr_… } (or the subject itself) names the creator (moderators, or the author naming themselves); null or false: open.",++ "anyOf": [++ { "type": ["boolean", "null"] },++ { "type": "string", "pattern": "^usr_" },++ { "type": "object", "required": ["owner"], "properties": { "owner": { "type": "string", "pattern": "^usr_" } } }++ ]++ },++ "category": { "type": "string", "description": "A new thread only: the slug of one of the space's categories." },++ "attachments": {++ "description": "At most 4 images the writer uploaded (POST /api/v1/spaces/:space/attachments) and has not used yet, by media id. People only.",++ "anyOf": [++ { "type": "string", "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$" },++ { "type": "array", "maxItems": 4, "items": { "type": "string", "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$" } }++ ]++ },++ "pastes": {++ "description": "At most 4 shareable pastes (not private, not burn-after-read) by slug or /p/<slug> link: an array, or one string separated by spaces or commas.",++ "type": ["array", "string"],++ "items": { "type": "string" }++ }++ }++ },++ {++ "type": "object",++ "required": ["value"],++ "properties": {++ "value": { "enum": [1, -1, 0, "1", "-1", "0"], "description": "A thread vote; 0 takes the vote back." }++ }++ },++ { "$ref": "../common/no-body.v1.json" }++ ]++}nnew file mode 100644@@@ -0,0 +1,92 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/post-write-result.v1.json",++ "title": "SpacePostWriteResult",++ "description": "space.post-write-result@1: answers of the forum writes on OpenVibe.Space (space.post.write). POST /api/v1/spaces/:space/threads → 201 { thread, post } (the thread and its opening post); POST …/threads/:slug/posts → 201 { post, page, url } (the reply, the page it lands on and its address with #post-<id>); PUT /api/v1/posts/:id → { post }; POST …/threads/:slug/votes → { thread_id, score, upvotes, downvotes, my_vote }; DELETE /api/v1/posts/:id → { ok, id } (deleting the opening post deletes the thread: { ok, id, deleted: thread }); DELETE /api/v1/spaces/:space/threads/:slug → { ok, id, deleted: thread }. Refusals are problem+json.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "thread",++ "post"++ ],++ "properties": {++ "thread": {++ "$ref": "forum-thread.v1.json"++ },++ "post": {++ "$ref": "forum-post.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "post"++ ],++ "properties": {++ "post": {++ "$ref": "forum-post.v1.json"++ },++ "page": {++ "type": "integer",++ "minimum": 1++ },++ "url": {++ "type": "string"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "thread_id",++ "score",++ "upvotes",++ "downvotes",++ "my_vote"++ ],++ "properties": {++ "thread_id": {++ "type": "integer"++ },++ "score": {++ "type": "integer"++ },++ "upvotes": {++ "type": "integer",++ "minimum": 0++ },++ "downvotes": {++ "type": "integer",++ "minimum": 0++ },++ "my_vote": {++ "enum": [++ 1,++ -1,++ 0++ ]++ }++ }++ },++ {++ "type": "object",++ "required": [++ "ok",++ "id"++ ],++ "properties": {++ "ok": {++ "const": true++ },++ "id": {++ "type": "integer"++ },++ "deleted": {++ "const": "thread"++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,134 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/thread-read-result.v1.json",++ "title": "SpaceThreadReadResult",++ "description": "space.thread-read-result@1: answers of space.thread.read on OpenVibe.Space. 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 @@++{ "id": 1, "thread_id": 1, "is_opening": false, "origin": "user", "author": null, "body_markdown": null, "body_html": null, "revision": 0, "deleted": true, "can_edit": false, "attachments": [], "pastes": [] }nnew file mode 100644@@@ -0,0 +1 @@++{ "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 }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 @@++{ "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": 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": 2 }nnew file mode 100644@@@ -0,0 +1 @@++{ "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 @@++{ "body": "x", "attachments": ["med_1", "med_2"] }nnew file mode 100644@@@ -0,0 +1 @@++{ "value": 2 }nnew file mode 100644@@@ -0,0 +1 @@++{ "title": "Restreaming to three platforms at once", "body": "What bitrates do you use?", "category": "help", "pastes": ["k3JfQ2"] }nnew file mode 100644@@@ -0,0 +1 @@++{ "body_markdown": "6000 kbps works for me", "attachments": ["med_01JAB2C3D4E5F6G7H8J9K0MNPQ"] }nnew file mode 100644@@@ -0,0 +1 @@++{ "value": -1 }nnew file mode 100644@@@ -0,0 +1 @@++{ "ok": true, "id": 311, "deleted": "post" }nnew file mode 100644@@@ -0,0 +1 @@++{ "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 }, "post": { "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 } }nnew file mode 100644@@@ -0,0 +1 @@++{ "post": { "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": false, "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 }, "page": 1, "url": "/s/streaming/t/restreaming-to-three-platforms#post-5121" }nnew file mode 100644@@@ -0,0 +1 @@++{ "ok": true, "id": 311, "deleted": "thread" }nnew file mode 100644@@@ -0,0 +1 @@++{ "thread_id": 311, "score": 3, "upvotes": 4, "downvotes": 1, "my_vote": 1 }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 } ] }@@@ -51068,7 +51068,7 @@"community.post-write-request": {"version": "1.0.0","owner": "community",-- "status": "active",++ "status": "deprecated","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/post-write-request.v1.json",@@@ -51235,7 +51235,7 @@"community.forum-post": {"version": "1.0.0","owner": "community",-- "status": "active",++ "status": "deprecated","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/forum-post.v1.json",@@@ -51430,7 +51430,7 @@"community.forum-thread": {"version": "1.0.0","owner": "community",-- "status": "active",++ "status": "deprecated","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/forum-thread.v1.json",@@@ -51715,7 +51715,7 @@"community.post-write-result": {"version": "1.0.0","owner": "community",-- "status": "active",++ "status": "deprecated","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/post-write-result.v1.json",@@@ -52304,7 +52304,7 @@"community.forum-space": {"version": "1.0.0","owner": "community",-- "status": "active",++ "status": "deprecated","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/forum-space.v1.json",@@@ -52606,7 +52606,7 @@"community.space-read-result": {"version": "1.0.0","owner": "community",-- "status": "active",++ "status": "deprecated","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/space-read-result.v1.json",@@@ -52681,7 +52681,7 @@"community.thread-read-result": {"version": "1.0.0","owner": "community",-- "status": "active",++ "status": "deprecated","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/thread-read-result.v1.json",@@@ -52817,6 +52817,1034 @@]}},++ "space.forum-post": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/forum-post.v1.json",++ "title": "SpaceForumPost",++ "description": "space.forum-post@1: one forum post (the opening post of a thread or a reply) as OpenVibe.Space's API shows it (server/forum/service.js shapePost). A deleted post keeps its place with no author, body, attachments or pastes.",++ "type": "object",++ "required": [++ "id",++ "thread_id",++ "is_opening",++ "origin",++ "author",++ "body_markdown",++ "body_html",++ "revision",++ "deleted",++ "can_edit",++ "attachments",++ "pastes"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "thread_id": {++ "type": "integer"++ },++ "is_opening": {++ "type": "boolean"++ },++ "origin": {++ "type": "string"++ },++ "author": {++ "type": [++ "object",++ "null"++ ],++ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",++ "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++ }++ }++ },++ "body_markdown": {++ "type": [++ "string",++ "null"++ ]++ },++ "body_html": {++ "type": [++ "string",++ "null"++ ]++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "deleted": {++ "type": "boolean"++ },++ "created_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "can_edit": {++ "type": "boolean"++ },++ "attachments": {++ "type": "array",++ "maxItems": 4,++ "items": {++ "type": "object",++ "required": [++ "media_id",++ "url"++ ],++ "properties": {++ "media_id": {++ "type": "string"++ },++ "url": {++ "type": "string"++ },++ "filename": {++ "type": [++ "string",++ "null"++ ]++ },++ "mime": {++ "type": "string"++ },++ "size_bytes": {++ "type": "integer"++ }++ }++ }++ },++ "pastes": {++ "type": "array",++ "maxItems": 4,++ "items": {++ "type": "object",++ "required": [++ "slug",++ "url"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "title": {++ "type": "string"++ },++ "language": {++ "type": "string"++ },++ "type": {++ "type": "string"++ },++ "url": {++ "type": "string"++ },++ "screenshot_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "lines": {++ "type": "integer"++ },++ "excerpt": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ }++ }++ }++ }++ },++ "space.forum-read-result": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/forum-read-result.v1.json",++ "title": "SpaceForumReadResult",++ "description": "space.forum-read-result@1: answers of space.forum.read on OpenVibe.Space. 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."++ }++ ]++ }++ },++ "space.forum-space": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/forum-space.v1.json",++ "title": "SpaceForumSpace",++ "description": "space.forum-space@1: one forum space as OpenVibe.Space'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"++ }++ }++ }++ },++ "space.forum-thread": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/forum-thread.v1.json",++ "title": "SpaceForumThread",++ "description": "space.forum-thread@1: one forum thread as OpenVibe.Space's /api/v1/spaces API shows it (server/forum/service.js shapeThread): where it lives, its kind and status, category, members-only gate, author, origin, pin and lock state, score, reply count, views, pages, the last post and the caller's vote.",++ "type": "object",++ "required": [++ "id",++ "space",++ "slug",++ "title",++ "url",++ "kind",++ "author",++ "origin",++ "pinned",++ "locked",++ "score",++ "reply_count",++ "pages",++ "created_at",++ "my_vote"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "space": {++ "type": "string"++ },++ "slug": {++ "type": "string"++ },++ "title": {++ "type": "string",++ "maxLength": 200++ },++ "url": {++ "type": "string",++ "description": "/s/<space>/t/<slug>"++ },++ "kind": {++ "type": "string",++ "description": "discussion, request, roadmap, …"++ },++ "status": {++ "type": [++ "string",++ "null"++ ]++ },++ "category": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "description": {++ "type": [++ "string",++ "null"++ ]++ },++ "position": {},++ "thread_count": {++ "type": "integer"++ }++ }++ },++ "members_only": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "owner": {++ "type": "string"++ },++ "owner_username": {++ "type": [++ "string",++ "null"++ ]++ },++ "join_url": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ },++ "author": {++ "type": [++ "object",++ "null"++ ],++ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",++ "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++ }++ }++ },++ "origin": {++ "type": "string"++ },++ "pinned": {++ "type": "boolean"++ },++ "locked": {++ "type": "boolean"++ },++ "score": {++ "type": "integer"++ },++ "reply_count": {++ "type": "integer",++ "minimum": 0++ },++ "views": {++ "type": "integer",++ "minimum": 0++ },++ "pages": {++ "type": "integer",++ "minimum": 1++ },++ "last_post": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "author": {++ "type": [++ "object",++ "null"++ ],++ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",++ "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++ }++ }++ }++ }++ },++ "crosspost_of": {},++ "last_activity_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "created_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "my_vote": {++ "enum": [++ 1,++ -1,++ 0++ ]++ }++ }++ }++ },++ "space.post-write-request": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/post-write-request.v1.json",++ "title": "SpacePostWriteRequest",++ "description": "space.post-write-request@1: bodies of the forum writes on OpenVibe.Space (space.post.write; a service writes as X-OV-Subject, or as AI with X-OV-Origin: ai). POST /api/v1/spaces/:space/threads: { title, body, members_only?, category?, attachments?, pastes? } (title 3 to 200 characters after whitespace is collapsed; body Markdown, non-blank, at most 40000 characters; body_markdown is accepted for body). POST …/threads/:slug/posts (a reply) and PUT /api/v1/posts/:id (an edit): { body, attachments?, pastes? }. POST …/threads/:slug/votes: { value: 1 | -1 | 0 }. DELETE /api/v1/posts/:id and DELETE /api/v1/spaces/:space/threads/:slug take no body. Unknown fields are ignored; refusals are problem+json (post.empty, post.too_long, thread.invalid_title, vote.invalid, thread.locked, vip.members_only, …).",++ "anyOf": [++ {++ "type": "object",++ "anyOf": [++ {++ "required": [++ "body"++ ]++ },++ {++ "required": [++ "body_markdown"++ ]++ }++ ],++ "properties": {++ "title": {++ "type": "string",++ "description": "A new thread's title."++ },++ "body": {++ "type": "string",++ "minLength": 1,++ "maxLength": 40000,++ "description": "Markdown."++ },++ "body_markdown": {++ "type": "string",++ "minLength": 1,++ "maxLength": 40000,++ "description": "Read when body is absent."++ },++ "members_only": {++ "description": "A new thread only: true gates it to the author's own VIP members; { owner: usr_… } (or the subject itself) names the creator (moderators, or the author naming themselves); null or false: open.",++ "anyOf": [++ {++ "type": [++ "boolean",++ "null"++ ]++ },++ {++ "type": "string",++ "pattern": "^usr_"++ },++ {++ "type": "object",++ "required": [++ "owner"++ ],++ "properties": {++ "owner": {++ "type": "string",++ "pattern": "^usr_"++ }++ }++ }++ ]++ },++ "category": {++ "type": "string",++ "description": "A new thread only: the slug of one of the space's categories."++ },++ "attachments": {++ "description": "At most 4 images the writer uploaded (POST /api/v1/spaces/:space/attachments) and has not used yet, by media id. People only.",++ "anyOf": [++ {++ "type": "string",++ "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "array",++ "maxItems": 4,++ "items": {++ "type": "string",++ "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ }++ ]++ },++ "pastes": {++ "description": "At most 4 shareable pastes (not private, not burn-after-read) by slug or /p/<slug> link: an array, or one string separated by spaces or commas.",++ "type": [++ "array",++ "string"++ ],++ "items": {++ "type": "string"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "value"++ ],++ "properties": {++ "value": {++ "enum": [++ 1,++ -1,++ 0,++ "1",++ "-1",++ "0"++ ],++ "description": "A thread vote; 0 takes the vote back."++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "space.post-write-result": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/post-write-result.v1.json",++ "title": "SpacePostWriteResult",++ "description": "space.post-write-result@1: answers of the forum writes on OpenVibe.Space (space.post.write). POST /api/v1/spaces/:space/threads → 201 { thread, post } (the thread and its opening post); POST …/threads/:slug/posts → 201 { post, page, url } (the reply, the page it lands on and its address with #post-<id>); PUT /api/v1/posts/:id → { post }; POST …/threads/:slug/votes → { thread_id, score, upvotes, downvotes, my_vote }; DELETE /api/v1/posts/:id → { ok, id } (deleting the opening post deletes the thread: { ok, id, deleted: thread }); DELETE /api/v1/spaces/:space/threads/:slug → { ok, id, deleted: thread }. Refusals are problem+json.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "thread",++ "post"++ ],++ "properties": {++ "thread": {++ "$ref": "forum-thread.v1.json"++ },++ "post": {++ "$ref": "forum-post.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "post"++ ],++ "properties": {++ "post": {++ "$ref": "forum-post.v1.json"++ },++ "page": {++ "type": "integer",++ "minimum": 1++ },++ "url": {++ "type": "string"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "thread_id",++ "score",++ "upvotes",++ "downvotes",++ "my_vote"++ ],++ "properties": {++ "thread_id": {++ "type": "integer"++ },++ "score": {++ "type": "integer"++ },++ "upvotes": {++ "type": "integer",++ "minimum": 0++ },++ "downvotes": {++ "type": "integer",++ "minimum": 0++ },++ "my_vote": {++ "enum": [++ 1,++ -1,++ 0++ ]++ }++ }++ },++ {++ "type": "object",++ "required": [++ "ok",++ "id"++ ],++ "properties": {++ "ok": {++ "const": true++ },++ "id": {++ "type": "integer"++ },++ "deleted": {++ "const": "thread"++ }++ }++ }++ ]++ }++ },++ "space.thread-read-result": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/thread-read-result.v1.json",++ "title": "SpaceThreadReadResult",++ "description": "space.thread-read-result@1: answers of space.thread.read on OpenVibe.Space. 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",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Billing API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Blog API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Chat API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Codes API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Community API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -855,18 +855,15 @@"delete": {"operationId": "delete_api_v1_posts_id","summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",-- "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.\n\n**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",++ "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.","tags": [-- "community.comment",-- "community.post"++ "community.comment"],"x-openvibe-capabilities": [-- "community.comment.moderate",-- "community.post.create"++ "community.comment.moderate"],"x-openvibe-visibility": [-- "internal",-- "public"++ "internal"],"x-openvibe-quota-class": "community-moderate","security": [@@@ -874,22 +871,10 @@"openvibeToken": ["community.comment.moderate"]-- },-- {-- "openvibeToken": [-- "community.post.create"-- ]}],"x-openvibe-input": {-- "anyOf": [-- {-- "$ref": "#/components/schemas/community.moderation-request.v1"-- },-- {-- "$ref": "#/components/schemas/community.post-write-request.v1"-- }-- ]++ "$ref": "#/components/schemas/community.moderation-request.v1"},"parameters": [{@@@ -907,14 +892,7 @@"content": {"application/json": {"schema": {-- "anyOf": [-- {-- "$ref": "#/components/schemas/community.moderation-result.v1"-- },-- {-- "$ref": "#/components/schemas/community.post-write-result.v1"-- }-- ]++ "$ref": "#/components/schemas/community.moderation-result.v1"}}}@@@ -934,18 +912,15 @@"put": {"operationId": "put_api_v1_posts_id","summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",-- "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.\n\n**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",++ "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.","tags": [-- "community.comment",-- "community.post"++ "community.comment"],"x-openvibe-capabilities": [-- "community.comment.moderate",-- "community.post.create"++ "community.comment.moderate"],"x-openvibe-visibility": [-- "internal",-- "public"++ "internal"],"x-openvibe-quota-class": "community-moderate","security": [@@@ -953,11 +928,6 @@"openvibeToken": ["community.comment.moderate"]-- },-- {-- "openvibeToken": [-- "community.post.create"-- ]}],"requestBody": {@@@ -965,14 +935,7 @@"content": {"application/json": {"schema": {-- "anyOf": [-- {-- "$ref": "#/components/schemas/community.moderation-request.v1"-- },-- {-- "$ref": "#/components/schemas/community.post-write-request.v1"-- }-- ]++ "$ref": "#/components/schemas/community.moderation-request.v1"}}}@@@ -993,14 +956,7 @@"content": {"application/json": {"schema": {-- "anyOf": [-- {-- "$ref": "#/components/schemas/community.moderation-result.v1"-- },-- {-- "$ref": "#/components/schemas/community.post-write-result.v1"-- }-- ]++ "$ref": "#/components/schemas/community.moderation-result.v1"}}}@@@ -1352,74 +1308,31 @@}}},-- "/api/v1/spaces": {-- "get": {-- "operationId": "get_api_v1_spaces",-- "summary": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*).",-- "description": "**community.space.read** (public): 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.",-- "tags": [-- "community.space"-- ],-- "x-openvibe-capabilities": [-- "community.space.read"-- ],-- "x-openvibe-visibility": [-- "public"-- ],-- "x-openvibe-quota-class": "community-read",-- "security": [-- {-- "openvibeToken": [-- "community.space.read"-- ]-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.space-read-result.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/api/v1/spaces/{space}": {-- "get": {-- "operationId": "get_api_v1_spaces_space",-- "summary": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*).",-- "description": "**community.space.read** (public): 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.",++ "/api/v1/spaces/{space}/threads/{slug}": {++ "delete": {++ "operationId": "delete_api_v1_spaces_space_threads_slug",++ "summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",++ "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.","tags": [-- "community.space"++ "community.comment"],"x-openvibe-capabilities": [-- "community.space.read"++ "community.comment.moderate"],"x-openvibe-visibility": [-- "public"++ "internal"],-- "x-openvibe-quota-class": "community-read",++ "x-openvibe-quota-class": "community-moderate","security": [{"openvibeToken": [-- "community.space.read"++ "community.comment.moderate"]}],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/community.moderation-request.v1"++ },"parameters": [{"name": "space",@@@ -1428,57 +1341,9 @@"schema": {"type": "string"}-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.space-read-result.v1"-- }-- }-- }},-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/api/v1/spaces/{space}/threads": {-- "get": {-- "operationId": "get_api_v1_spaces_space_threads",-- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",-- "description": "**community.thread.read** (public): 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.",-- "tags": [-- "community.thread"-- ],-- "x-openvibe-capabilities": [-- "community.thread.read"-- ],-- "x-openvibe-visibility": [-- "public"-- ],-- "x-openvibe-quota-class": "community-read",-- "security": [-- {-- "openvibeToken": [-- "community.thread.read"-- ]-- }-- ],-- "parameters": [{-- "name": "space",++ "name": "slug","in": "path","required": true,"schema": {@@@ -1492,7 +1357,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/community.thread-read-result.v1"++ "$ref": "#/components/schemas/community.moderation-result.v1"}}}@@@ -1508,25 +1373,27 @@}}}-- },-- "post": {-- "operationId": "post_api_v1_spaces_space_threads",-- "summary": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI",-- "description": "**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",++ }++ },++ "/api/v1/spaces/{space}/threads/{slug}/state": {++ "put": {++ "operationId": "put_api_v1_spaces_space_threads_slug_state",++ "summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",++ "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.","tags": [-- "community.post"++ "community.comment"],"x-openvibe-capabilities": [-- "community.post.create"++ "community.comment.moderate"],"x-openvibe-visibility": [-- "public"++ "internal"],-- "x-openvibe-quota-class": "community-write",++ "x-openvibe-quota-class": "community-moderate","security": [{"openvibeToken": [-- "community.post.create"++ "community.comment.moderate"]}],@@@ -1535,7 +1402,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/community.post-write-request.v1"++ "$ref": "#/components/schemas/community.moderation-request.v1"}}}@@@ -1548,6 +1415,14 @@"schema": {"type": "string"}++ },++ {++ "name": "slug",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }}],"responses": {@@@ -1556,7 +1431,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/community.post-write-result.v1"++ "$ref": "#/components/schemas/community.moderation-result.v1"}}}@@@ -1574,78 +1449,35 @@}}},-- "/api/v1/spaces/{space}/threads/{slug}": {-- "delete": {-- "operationId": "delete_api_v1_spaces_space_threads_slug",-- "summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",-- "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.\n\n**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",++ "/pulse": {++ "get": {++ "operationId": "get_pulse",++ "summary": "Read the network's public activity feed (Pulse): entity reference, title, URL and origin (user, ai or system), newest first with a cursor.",++ "description": "**community.pulse.read** (public): 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.","tags": [-- "community.comment",-- "community.post"++ "community.pulse"],"x-openvibe-capabilities": [-- "community.comment.moderate",-- "community.post.create"++ "community.pulse.read"],"x-openvibe-visibility": [-- "internal","public"],-- "x-openvibe-quota-class": "community-moderate",++ "x-openvibe-quota-class": "community-read","security": [{"openvibeToken": [-- "community.comment.moderate"-- ]-- },-- {-- "openvibeToken": [-- "community.post.create"++ "community.pulse.read"]}],-- "x-openvibe-input": {-- "anyOf": [-- {-- "$ref": "#/components/schemas/community.moderation-request.v1"-- },-- {-- "$ref": "#/components/schemas/community.post-write-request.v1"-- }-- ]-- },-- "parameters": [-- {-- "name": "space",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- },-- {-- "name": "slug",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],"responses": {"2XX": {"description": "Success","content": {"application/json": {"schema": {-- "anyOf": [-- {-- "$ref": "#/components/schemas/community.moderation-result.v1"-- },-- {-- "$ref": "#/components/schemas/community.post-write-result.v1"-- }-- ]++ "$ref": "#/components/schemas/community.pulse-read-result.v1"}}}@@@ -1661,1360 +1493,141 @@}}}-- },-- "get": {-- "operationId": "get_api_v1_spaces_space_threads_slug",-- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",-- "description": "**community.thread.read** (public): 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.",-- "tags": [-- "community.thread"-- ],-- "x-openvibe-capabilities": [-- "community.thread.read"-- ],-- "x-openvibe-visibility": [-- "public"-- ],-- "x-openvibe-quota-class": "community-read",-- "security": [-- {-- "openvibeToken": [-- "community.thread.read"-- ]-- }-- ],-- "parameters": [-- {-- "name": "space",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- },-- {-- "name": "slug",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.thread-read-result.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/api/v1/spaces/{space}/threads/{slug}/posts": {-- "post": {-- "operationId": "post_api_v1_spaces_space_threads_slug_posts",-- "summary": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI",-- "description": "**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",-- "tags": [-- "community.post"-- ],-- "x-openvibe-capabilities": [-- "community.post.create"-- ],-- "x-openvibe-visibility": [-- "public"-- ],-- "x-openvibe-quota-class": "community-write",-- "security": [-- {-- "openvibeToken": [-- "community.post.create"-- ]-- }-- ],-- "requestBody": {-- "required": false,-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.post-write-request.v1"-- }-- }-- }-- },-- "parameters": [-- {-- "name": "space",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- },-- {-- "name": "slug",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.post-write-result.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/api/v1/spaces/{space}/threads/{slug}/state": {-- "put": {-- "operationId": "put_api_v1_spaces_space_threads_slug_state",-- "summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",-- "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.",-- "tags": [-- "community.comment"-- ],-- "x-openvibe-capabilities": [-- "community.comment.moderate"-- ],-- "x-openvibe-visibility": [-- "internal"-- ],-- "x-openvibe-quota-class": "community-moderate",-- "security": [-- {-- "openvibeToken": [-- "community.comment.moderate"-- ]-- }-- ],-- "requestBody": {-- "required": false,-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.moderation-request.v1"-- }-- }-- }-- },-- "parameters": [-- {-- "name": "space",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- },-- {-- "name": "slug",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.moderation-result.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/api/v1/spaces/{space}/threads/{slug}/votes": {-- "post": {-- "operationId": "post_api_v1_spaces_space_threads_slug_votes",-- "summary": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI",-- "description": "**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",-- "tags": [-- "community.post"-- ],-- "x-openvibe-capabilities": [-- "community.post.create"-- ],-- "x-openvibe-visibility": [-- "public"-- ],-- "x-openvibe-quota-class": "community-write",-- "security": [-- {-- "openvibeToken": [-- "community.post.create"-- ]-- }-- ],-- "requestBody": {-- "required": false,-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.post-write-request.v1"-- }-- }-- }-- },-- "parameters": [-- {-- "name": "space",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- },-- {-- "name": "slug",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.post-write-result.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/pulse": {-- "get": {-- "operationId": "get_pulse",-- "summary": "Read the network's public activity feed (Pulse): entity reference, title, URL and origin (user, ai or system), newest first with a cursor.",-- "description": "**community.pulse.read** (public): 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.",-- "tags": [-- "community.pulse"-- ],-- "x-openvibe-capabilities": [-- "community.pulse.read"-- ],-- "x-openvibe-visibility": [-- "public"-- ],-- "x-openvibe-quota-class": "community-read",-- "security": [-- {-- "openvibeToken": [-- "community.pulse.read"-- ]-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.pulse-read-result.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/s": {-- "get": {-- "operationId": "get_s",-- "summary": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*).",-- "description": "**community.space.read** (public): 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.",-- "tags": [-- "community.space"-- ],-- "x-openvibe-capabilities": [-- "community.space.read"-- ],-- "x-openvibe-visibility": [-- "public"-- ],-- "x-openvibe-quota-class": "community-read",-- "security": [-- {-- "openvibeToken": [-- "community.space.read"-- ]-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.space-read-result.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/s/{space}": {-- "get": {-- "operationId": "get_s_space",-- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",-- "description": "**community.thread.read** (public): 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.",-- "tags": [-- "community.thread"-- ],-- "x-openvibe-capabilities": [-- "community.thread.read"-- ],-- "x-openvibe-visibility": [-- "public"-- ],-- "x-openvibe-quota-class": "community-read",-- "security": [-- {-- "openvibeToken": [-- "community.thread.read"-- ]-- }-- ],-- "parameters": [-- {-- "name": "space",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.thread-read-result.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/s/{space}/feed.xml": {-- "get": {-- "operationId": "get_s_space_feed_xml",-- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",-- "description": "**community.thread.read** (public): 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.",-- "tags": [-- "community.thread"-- ],-- "x-openvibe-capabilities": [-- "community.thread.read"-- ],-- "x-openvibe-visibility": [-- "public"-- ],-- "x-openvibe-quota-class": "community-read",-- "security": [-- {-- "openvibeToken": [-- "community.thread.read"-- ]-- }-- ],-- "parameters": [-- {-- "name": "space",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.thread-read-result.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/s/{space}/t/{slug}": {-- "get": {-- "operationId": "get_s_space_t_slug",-- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",-- "description": "**community.thread.read** (public): 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.",-- "tags": [-- "community.thread"-- ],-- "x-openvibe-capabilities": [-- "community.thread.read"-- ],-- "x-openvibe-visibility": [-- "public"-- ],-- "x-openvibe-quota-class": "community-read",-- "security": [-- {-- "openvibeToken": [-- "community.thread.read"-- ]-- }-- ],-- "parameters": [-- {-- "name": "space",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- },-- {-- "name": "slug",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.thread-read-result.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/s/feed.xml": {-- "get": {-- "operationId": "get_s_feed_xml",-- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",-- "description": "**community.thread.read** (public): 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.",-- "tags": [-- "community.thread"-- ],-- "x-openvibe-capabilities": [-- "community.thread.read"-- ],-- "x-openvibe-visibility": [-- "public"-- ],-- "x-openvibe-quota-class": "community-read",-- "security": [-- {-- "openvibeToken": [-- "community.thread.read"-- ]-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/community.thread-read-result.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- }-- },-- "components": {-- "schemas": {-- "common.entity-ref.v1": {-- "title": "EntityRef",-- "description": "Typed reference to another service's entity. Store this instead of a foreign key; resolve a display projection from the owner.",-- "type": "object",-- "required": [-- "service",-- "type",-- "id"-- ],-- "additionalProperties": false,-- "properties": {-- "service": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9-]{1,39}$"-- },-- "type": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9_]{1,39}$"-- },-- "id": {-- "type": "string",-- "minLength": 1,-- "maxLength": 128-- },-- "revision": {-- "type": "integer",-- "minimum": 0-- },-- "label": {-- "type": "string",-- "maxLength": 200,-- "description": "Cached display label. Never authoritative."-- }-- }-- },-- "common.no-body.v1": {-- "title": "NoBody",-- "description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",-- "type": "object",-- "maxProperties": 0-- },-- "community.comment-thread.v1": {-- "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": "#/components/schemas/common.entity-ref.v1"-- },-- "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.v1": {-- "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": "#/components/schemas/common.entity-ref.v1"-- }-- }-- },-- {-- "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": "#/components/schemas/common.no-body.v1"-- }-- ]-- },-- "community.comment-write-result.v1": {-- "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": "#/components/schemas/community.comment-thread.v1"-- },-- "created": {-- "type": "boolean"-- }-- }-- },-- {-- "type": "object",-- "required": [-- "thread",-- "comments",-- "next_cursor",-- "viewer"-- ],-- "properties": {-- "thread": {-- "$ref": "#/components/schemas/community.comment-thread.v1"-- },-- "comments": {-- "type": "array",-- "items": {-- "$ref": "#/components/schemas/community.comment.v1"-- }-- },-- "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": "#/components/schemas/community.comment.v1"-- }-- }-- },-- {-- "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.v1": {-- "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-post.v1": {-- "title": "CommunityForumPost",-- "description": "community.forum-post@1: one forum post (the opening post of a thread or a reply) as OpenVibe.Community's API shows it (server/forum/service.js shapePost). A deleted post keeps its place with no author, body, attachments or pastes.",-- "type": "object",-- "required": [-- "id",-- "thread_id",-- "is_opening",-- "origin",-- "author",-- "body_markdown",-- "body_html",-- "revision",-- "deleted",-- "can_edit",-- "attachments",-- "pastes"++ }++ }++ },++ "components": {++ "schemas": {++ "common.entity-ref.v1": {++ "title": "EntityRef",++ "description": "Typed reference to another service's entity. Store this instead of a foreign key; resolve a display projection from the owner.",++ "type": "object",++ "required": [++ "service",++ "type",++ "id"],++ "additionalProperties": false,"properties": {-- "id": {-- "type": "integer"-- },-- "thread_id": {-- "type": "integer"-- },-- "is_opening": {-- "type": "boolean"-- },-- "origin": {-- "type": "string"-- },-- "author": {-- "type": [-- "object",-- "null"-- ],-- "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",-- "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-- }-- }++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"},-- "body_markdown": {-- "type": [-- "string",-- "null"-- ]++ "type": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]{1,39}$"},-- "body_html": {-- "type": [-- "string",-- "null"-- ]++ "id": {++ "type": "string",++ "minLength": 1,++ "maxLength": 128},"revision": {"type": "integer",-- "minimum": 1-- },-- "deleted": {-- "type": "boolean"-- },-- "created_at": {-- "type": [-- "string",-- "null"-- ],-- "format": "date-time"-- },-- "updated_at": {-- "type": [-- "string",-- "null"-- ],-- "format": "date-time"-- },-- "can_edit": {-- "type": "boolean"-- },-- "attachments": {-- "type": "array",-- "maxItems": 4,-- "items": {-- "type": "object",-- "required": [-- "media_id",-- "url"-- ],-- "properties": {-- "media_id": {-- "type": "string"-- },-- "url": {-- "type": "string"-- },-- "filename": {-- "type": [-- "string",-- "null"-- ]-- },-- "mime": {-- "type": "string"-- },-- "size_bytes": {-- "type": "integer"-- }-- }-- }++ "minimum": 0},-- "pastes": {-- "type": "array",-- "maxItems": 4,-- "items": {-- "type": "object",-- "required": [-- "slug",-- "url"-- ],-- "properties": {-- "slug": {-- "type": "string"-- },-- "title": {-- "type": "string"-- },-- "language": {-- "type": "string"-- },-- "type": {-- "type": "string"-- },-- "url": {-- "type": "string"-- },-- "screenshot_url": {-- "type": [-- "string",-- "null"-- ]-- },-- "lines": {-- "type": "integer"-- },-- "excerpt": {-- "type": [-- "string",-- "null"-- ]-- }-- }-- }++ "label": {++ "type": "string",++ "maxLength": 200,++ "description": "Cached display label. Never authoritative."}}},-- "community.forum-space.v1": {-- "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.",++ "common.no-body.v1": {++ "title": "NoBody",++ "description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",++ "type": "object",++ "maxProperties": 0++ },++ "community.comment-thread.v1": {++ "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": [-- "slug",-- "name",++ "id",++ "access_id",++ "ref","visibility",-- "url",-- "thread_kind",-- "statuses",-- "style",-- "votes",-- "reactions",-- "group",-- "parent",-- "members_only"++ "comment_count"],"properties": {-- "slug": {-- "type": "string"-- },-- "name": {-- "type": "string"-- },-- "description": {++ "id": {"type": [-- "string",-- "null"-- ]-- },-- "visibility": {-- "enum": [-- "public",-- "members",-- "staff"++ "integer",++ "string"]},-- "url": {-- "type": "string"-- },-- "thread_kind": {-- "type": "string"++ "access_id": {++ "type": "string",++ "pattern": "^cth_"},-- "statuses": {-- "type": "array",-- "items": {-- "type": "string"-- }++ "ref": {++ "$ref": "#/components/schemas/common.entity-ref.v1"},-- "style": {++ "visibility": {"enum": [-- "feed",-- "forum"-- ]-- },-- "votes": {-- "type": "boolean"-- },-- "reactions": {-- "type": "boolean"-- },-- "group": {-- "type": [-- "object",-- "null"-- ],-- "properties": {-- "slug": {-- "type": "string"-- },-- "name": {-- "type": "string"-- }-- }++ "public",++ "hidden",++ "locked"++ ]},-- "parent": {++ "comment_count": {"type": [-- "object",++ "integer","null"-- ],-- "properties": {-- "slug": {-- "type": "string"-- },-- "name": {-- "type": "string"-- }-- }-- },-- "post_count": {-- "type": "integer"-- },-- "thread_count": {-- "type": "integer"++ ]},-- "last_activity_at": {++ "created_at": {"type": ["string","null"],"format": "date-time"},-- "members_only": {++ "updated_at": {"type": [-- "object",++ "string","null"],++ "format": "date-time"++ }++ }++ },++ "community.comment-write-request.v1": {++ "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": {-- "owner": {-- "type": "string"++ "ref": {++ "$ref": "#/components/schemas/common.entity-ref.v1"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "message"++ ],++ "properties": {++ "message": {++ "type": "string",++ "minLength": 1,++ "maxLength": 5000,++ "pattern": "\\S"},-- "owner_username": {++ "parent_id": {"type": [++ "integer","string","null"]},-- "join_url": {++ "anon_name": {"type": ["string","null"@@@ -3022,119 +1635,193 @@}}},-- "last_post": {-- "type": [-- "object",-- "null"-- ]++ {++ "type": "object",++ "required": [++ "value"++ ],++ "properties": {++ "value": {++ "enum": [++ 1,++ -1,++ 0,++ "1",++ "-1",++ "0"++ ]++ }++ }},-- "children": {-- "type": "array"++ {++ "$ref": "#/components/schemas/common.no-body.v1"}-- }++ ]},-- "community.forum-thread.v1": {-- "title": "CommunityForumThread",-- "description": "community.forum-thread@1: one forum thread as OpenVibe.Community's /api/v1/spaces API shows it (server/forum/service.js shapeThread): where it lives, its kind and status, category, members-only gate, author, origin, pin and lock state, score, reply count, views, pages, the last post and the caller's vote.",-- "type": "object",-- "required": [-- "id",-- "space",-- "slug",-- "title",-- "url",-- "kind",-- "author",-- "origin",-- "pinned",-- "locked",-- "score",-- "reply_count",-- "pages",-- "created_at",-- "my_vote"-- ],-- "properties": {-- "id": {-- "type": "integer"-- },-- "space": {-- "type": "string"-- },-- "slug": {-- "type": "string"-- },-- "title": {-- "type": "string",-- "maxLength": 200-- },-- "url": {-- "type": "string",-- "description": "/s/<space>/t/<slug>"-- },-- "kind": {-- "type": "string",-- "description": "discussion, request, roadmap, …"-- },-- "status": {-- "type": [-- "string",-- "null"-- ]++ "community.comment-write-result.v1": {++ "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": "#/components/schemas/community.comment-thread.v1"++ },++ "created": {++ "type": "boolean"++ }++ }},-- "category": {-- "type": [-- "object",-- "null"++ {++ "type": "object",++ "required": [++ "thread",++ "comments",++ "next_cursor",++ "viewer"],"properties": {-- "slug": {-- "type": "string"++ "thread": {++ "$ref": "#/components/schemas/community.comment-thread.v1"},-- "name": {-- "type": "string"++ "comments": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/community.comment.v1"++ }},-- "description": {++ "next_cursor": {"type": ["string","null"]},-- "position": {},-- "thread_count": {++ "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": "#/components/schemas/community.comment.v1"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "ok",++ "id"++ ],++ "properties": {++ "ok": {++ "const": true++ },++ "id": {"type": "integer"}}},-- "members_only": {-- "type": [-- "object",-- "null"++ {++ "type": "object",++ "required": [++ "comment_id",++ "score",++ "upvotes",++ "downvotes",++ "my_vote"],"properties": {-- "owner": {-- "type": "string"++ "comment_id": {++ "type": "integer"},-- "owner_username": {-- "type": [-- "string",-- "null"-- ]++ "score": {++ "type": "integer"},-- "join_url": {-- "type": [-- "string",-- "null"++ "upvotes": {++ "type": "integer",++ "minimum": 0++ },++ "downvotes": {++ "type": "integer",++ "minimum": 0++ },++ "my_vote": {++ "enum": [++ 1,++ -1,++ 0]}}++ }++ ]++ },++ "community.comment.v1": {++ "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"],-- "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.","properties": {"subject": {"type": [@@@ -3174,107 +1861,84 @@}}},-- "origin": {-- "type": "string"++ "anon_name": {++ "type": [++ "string",++ "null"++ ]},-- "pinned": {-- "type": "boolean"++ "display_name": {++ "type": [++ "string",++ "null"++ ]},-- "locked": {++ "message": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 5000++ },++ "deleted": {"type": "boolean"},"score": {"type": "integer"},-- "reply_count": {++ "upvotes": {"type": "integer","minimum": 0},-- "views": {++ "downvotes": {"type": "integer","minimum": 0},-- "pages": {-- "type": "integer",-- "minimum": 1++ "my_vote": {++ "enum": [++ 1,++ -1,++ 0++ ]},-- "last_post": {++ "reply_count": {"type": [-- "object",++ "integer",++ "null"++ ]++ },++ "created_at": {++ "type": [++ "string","null"],-- "properties": {-- "id": {-- "type": "integer"-- },-- "author": {-- "type": [-- "object",-- "null"-- ],-- "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",-- "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-- }-- }-- }-- }++ "format": "date-time"},-- "crosspost_of": {},-- "last_activity_at": {++ "updated_at": {"type": ["string","null"],"format": "date-time"},-- "created_at": {++ "edited_at": {"type": ["string","null"],"format": "date-time"},-- "my_vote": {-- "enum": [-- 1,-- -1,-- 0-- ]++ "can_edit": {++ "type": "boolean"++ },++ "can_delete": {++ "type": "boolean"++ },++ "replies": {++ "type": "array",++ "items": {++ "type": "object"++ }}}},@@@ -4196,283 +2860,74 @@"type": "boolean"},"ai_summary": {-- "type": [-- "string",-- "null"-- ]-- },-- "ai_tags": {-- "type": [-- "string",-- "null"-- ],-- "description": "JSON text as the AI pass wrote it."-- },-- "ai_analyzed_at": {-- "type": [-- "string",-- "null"-- ]-- },-- "revision": {-- "type": "integer"-- },-- "url": {-- "type": "string",-- "description": "/p/<slug>"-- },-- "raw_url": {-- "type": "string",-- "description": "/p/<slug>/raw"-- },-- "created_at": {-- "type": "string"-- },-- "updated_at": {-- "type": [-- "string",-- "null"-- ]-- },-- "is_owner": {-- "type": "boolean",-- "description": "Whether the caller owns it."-- },-- "username": {-- "type": [-- "string",-- "null"-- ]-- },-- "display_name": {-- "type": [-- "string",-- "null"-- ],-- "description": "OpenVibe AI for an AI paste."-- },-- "avatar_url": {-- "type": [-- "string",-- "null"-- ]-- },-- "profile_color": {-- "type": [-- "string",-- "null"-- ]-- }-- }-- },-- "community.post-write-request.v1": {-- "title": "CommunityPostWriteRequest",-- "description": "community.post-write-request@1: bodies of the forum writes on OpenVibe.Community (community.post.create; a service writes as X-OV-Subject, or as AI with X-OV-Origin: ai). POST /api/v1/spaces/:space/threads: { title, body, members_only?, category?, attachments?, pastes? } (title 3 to 200 characters after whitespace is collapsed; body Markdown, non-blank, at most 40000 characters; body_markdown is accepted for body). POST …/threads/:slug/posts (a reply) and PUT /api/v1/posts/:id (an edit): { body, attachments?, pastes? }. POST …/threads/:slug/votes: { value: 1 | -1 | 0 }. DELETE /api/v1/posts/:id and DELETE /api/v1/spaces/:space/threads/:slug take no body. Unknown fields are ignored; refusals are problem+json (post.empty, post.too_long, thread.invalid_title, vote.invalid, thread.locked, vip.members_only, …).",-- "anyOf": [-- {-- "type": "object",-- "anyOf": [-- {-- "required": [-- "body"-- ]-- },-- {-- "required": [-- "body_markdown"-- ]-- }-- ],-- "properties": {-- "title": {-- "type": "string",-- "description": "A new thread's title."-- },-- "body": {-- "type": "string",-- "minLength": 1,-- "maxLength": 40000,-- "description": "Markdown."-- },-- "body_markdown": {-- "type": "string",-- "minLength": 1,-- "maxLength": 40000,-- "description": "Read when body is absent."-- },-- "members_only": {-- "description": "A new thread only: true gates it to the author's own VIP members; { owner: usr_… } (or the subject itself) names the creator (moderators, or the author naming themselves); null or false: open.",-- "anyOf": [-- {-- "type": [-- "boolean",-- "null"-- ]-- },-- {-- "type": "string",-- "pattern": "^usr_"-- },-- {-- "type": "object",-- "required": [-- "owner"-- ],-- "properties": {-- "owner": {-- "type": "string",-- "pattern": "^usr_"-- }-- }-- }-- ]-- },-- "category": {-- "type": "string",-- "description": "A new thread only: the slug of one of the space's categories."-- },-- "attachments": {-- "description": "At most 4 images the writer uploaded (POST /api/v1/spaces/:space/attachments) and has not used yet, by media id. People only.",-- "anyOf": [-- {-- "type": "string",-- "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- {-- "type": "array",-- "maxItems": 4,-- "items": {-- "type": "string",-- "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"-- }-- }-- ]-- },-- "pastes": {-- "description": "At most 4 shareable pastes (not private, not burn-after-read) by slug or /p/<slug> link: an array, or one string separated by spaces or commas.",-- "type": [-- "array",-- "string"-- ],-- "items": {-- "type": "string"-- }-- }-- }-- },-- {-- "type": "object",-- "required": [-- "value"-- ],-- "properties": {-- "value": {-- "enum": [-- 1,-- -1,-- 0,-- "1",-- "-1",-- "0"-- ],-- "description": "A thread vote; 0 takes the vote back."-- }-- }++ "type": [++ "string",++ "null"++ ]},-- {-- "$ref": "#/components/schemas/common.no-body.v1"-- }-- ]-- },-- "community.post-write-result.v1": {-- "title": "CommunityPostWriteResult",-- "description": "community.post-write-result@1: answers of the forum writes on OpenVibe.Community (community.post.create). POST /api/v1/spaces/:space/threads → 201 { thread, post } (the thread and its opening post); POST …/threads/:slug/posts → 201 { post, page, url } (the reply, the page it lands on and its address with #post-<id>); PUT /api/v1/posts/:id → { post }; POST …/threads/:slug/votes → { thread_id, score, upvotes, downvotes, my_vote }; DELETE /api/v1/posts/:id → { ok, id } (deleting the opening post deletes the thread: { ok, id, deleted: thread }); DELETE /api/v1/spaces/:space/threads/:slug → { ok, id, deleted: thread }. Refusals are problem+json.",-- "anyOf": [-- {-- "type": "object",-- "required": [-- "thread",-- "post"++ "ai_tags": {++ "type": [++ "string",++ "null"],-- "properties": {-- "thread": {-- "$ref": "#/components/schemas/community.forum-thread.v1"-- },-- "post": {-- "$ref": "#/components/schemas/community.forum-post.v1"-- }-- }++ "description": "JSON text as the AI pass wrote it."},-- {-- "type": "object",-- "required": [-- "post"-- ],-- "properties": {-- "post": {-- "$ref": "#/components/schemas/community.forum-post.v1"-- },-- "page": {-- "type": "integer",-- "minimum": 1-- },-- "url": {-- "type": "string"-- }-- }++ "ai_analyzed_at": {++ "type": [++ "string",++ "null"++ ]},-- {-- "type": "object",-- "required": [-- "thread_id",-- "score",-- "upvotes",-- "downvotes",-- "my_vote"-- ],-- "properties": {-- "thread_id": {-- "type": "integer"-- },-- "score": {-- "type": "integer"-- },-- "upvotes": {-- "type": "integer",-- "minimum": 0-- },-- "downvotes": {-- "type": "integer",-- "minimum": 0-- },-- "my_vote": {-- "enum": [-- 1,-- -1,-- 0-- ]-- }-- }++ "revision": {++ "type": "integer"},-- {-- "type": "object",-- "required": [-- "ok",-- "id"++ "url": {++ "type": "string",++ "description": "/p/<slug>"++ },++ "raw_url": {++ "type": "string",++ "description": "/p/<slug>/raw"++ },++ "created_at": {++ "type": "string"++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ]++ },++ "is_owner": {++ "type": "boolean",++ "description": "Whether the caller owns it."++ },++ "username": {++ "type": [++ "string",++ "null"++ ]++ },++ "display_name": {++ "type": [++ "string",++ "null"],-- "properties": {-- "ok": {-- "const": true-- },-- "id": {-- "type": "integer"-- },-- "deleted": {-- "const": "thread"-- }-- }++ "description": "OpenVibe AI for an AI paste."++ },++ "avatar_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "profile_color": {++ "type": [++ "string",++ "null"++ ]}-- ]++ }},"community.pulse-item.v1": {"title": "CommunityPulseItem",@@@ -4706,206 +3161,6 @@}]},-- "community.space-read-result.v1": {-- "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": "#/components/schemas/community.forum-space.v1"-- }-- },-- "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": "#/components/schemas/community.forum-space.v1"-- }-- }-- },-- {-- "type": "string",-- "contentMediaType": "text/html",-- "description": "GET /s: the board index page."-- }-- ]-- },-- "community.thread-read-result.v1": {-- "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": "#/components/schemas/community.forum-thread.v1"-- }-- }-- }-- },-- {-- "type": "object",-- "required": [-- "space",-- "thread",-- "posts",-- "page",-- "pages"-- ],-- "properties": {-- "space": {-- "type": "object"-- },-- "thread": {-- "$ref": "#/components/schemas/community.forum-thread.v1"-- },-- "posts": {-- "type": "array",-- "items": {-- "$ref": "#/components/schemas/community.forum-post.v1"-- }-- },-- "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."-- }-- ]-- },"errors.problem.v1": {"title": "Problem","description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Coupons API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Deals API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Events API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Games API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Host API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -40,8 +40,8 @@"service": "community","name": "OpenVibe.Community","origin": "https://openvibe.community",-- "operations": 36,-- "capabilities": 10++ "operations": 24,++ "capabilities": 7},{"service": "coupons",@@@ -134,6 +134,13 @@"operations": 13,"capabilities": 3},++ {++ "service": "space",++ "name": "OpenVibe.Space",++ "origin": "https://openvibe.space",++ "operations": 15,++ "capabilities": 3++ },{"service": "tips","name": "OpenVibe.Tips",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Media API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Network API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.News API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenRe.Stream API",-- "version": "0.81.0",-- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Reviews API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Search API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Sources API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"nnew file mode 100644@@@ -0,0 +1,1973 @@++{++ "openapi": "3.1.0",++ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",++ "info": {++ "title": "OpenVibe.Space API",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "license": {++ "name": "MIT",++ "identifier": "MIT"++ }++ },++ "servers": [++ {++ "url": "https://openvibe.space"++ }++ ],++ "paths": {++ "/api/v1/posts/{id}": {++ "delete": {++ "operationId": "delete_api_v1_posts_id",++ "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",++ "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",++ "tags": [++ "space.post"++ ],++ "x-openvibe-capabilities": [++ "space.post.write"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-write",++ "security": [++ {++ "openvibeToken": [++ "space.post.write"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.post-write-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.post-write-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_api_v1_posts_id",++ "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",++ "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",++ "tags": [++ "space.post"++ ],++ "x-openvibe-capabilities": [++ "space.post.write"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-write",++ "security": [++ {++ "openvibeToken": [++ "space.post.write"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.post-write-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.post-write-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/spaces": {++ "get": {++ "operationId": "get_api_v1_spaces",++ "summary": "List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum).",++ "description": "**space.forum.read** (public): List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Space checks space visibility itself.",++ "tags": [++ "space.forum"++ ],++ "x-openvibe-capabilities": [++ "space.forum.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-read",++ "security": [++ {++ "openvibeToken": [++ "space.forum.read"++ ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.forum-read-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/spaces/{space}": {++ "get": {++ "operationId": "get_api_v1_spaces_space",++ "summary": "List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum).",++ "description": "**space.forum.read** (public): List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Space checks space visibility itself.",++ "tags": [++ "space.forum"++ ],++ "x-openvibe-capabilities": [++ "space.forum.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-read",++ "security": [++ {++ "openvibeToken": [++ "space.forum.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.forum-read-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/spaces/{space}/threads": {++ "get": {++ "operationId": "get_api_v1_spaces_space_threads",++ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",++ "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",++ "tags": [++ "space.thread"++ ],++ "x-openvibe-capabilities": [++ "space.thread.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-read",++ "security": [++ {++ "openvibeToken": [++ "space.thread.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.thread-read-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_api_v1_spaces_space_threads",++ "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",++ "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",++ "tags": [++ "space.post"++ ],++ "x-openvibe-capabilities": [++ "space.post.write"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-write",++ "security": [++ {++ "openvibeToken": [++ "space.post.write"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.post-write-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.post-write-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/spaces/{space}/threads/{slug}": {++ "delete": {++ "operationId": "delete_api_v1_spaces_space_threads_slug",++ "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",++ "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",++ "tags": [++ "space.post"++ ],++ "x-openvibe-capabilities": [++ "space.post.write"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-write",++ "security": [++ {++ "openvibeToken": [++ "space.post.write"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.post-write-request.v1"++ },++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "slug",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.post-write-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "get": {++ "operationId": "get_api_v1_spaces_space_threads_slug",++ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",++ "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",++ "tags": [++ "space.thread"++ ],++ "x-openvibe-capabilities": [++ "space.thread.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-read",++ "security": [++ {++ "openvibeToken": [++ "space.thread.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "slug",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.thread-read-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/spaces/{space}/threads/{slug}/posts": {++ "post": {++ "operationId": "post_api_v1_spaces_space_threads_slug_posts",++ "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",++ "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",++ "tags": [++ "space.post"++ ],++ "x-openvibe-capabilities": [++ "space.post.write"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-write",++ "security": [++ {++ "openvibeToken": [++ "space.post.write"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.post-write-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "slug",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.post-write-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/spaces/{space}/threads/{slug}/votes": {++ "post": {++ "operationId": "post_api_v1_spaces_space_threads_slug_votes",++ "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",++ "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",++ "tags": [++ "space.post"++ ],++ "x-openvibe-capabilities": [++ "space.post.write"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-write",++ "security": [++ {++ "openvibeToken": [++ "space.post.write"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.post-write-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "slug",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.post-write-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/s": {++ "get": {++ "operationId": "get_s",++ "summary": "List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum).",++ "description": "**space.forum.read** (public): List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Space checks space visibility itself.",++ "tags": [++ "space.forum"++ ],++ "x-openvibe-capabilities": [++ "space.forum.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-read",++ "security": [++ {++ "openvibeToken": [++ "space.forum.read"++ ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.forum-read-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/s/{space}": {++ "get": {++ "operationId": "get_s_space",++ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",++ "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",++ "tags": [++ "space.thread"++ ],++ "x-openvibe-capabilities": [++ "space.thread.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-read",++ "security": [++ {++ "openvibeToken": [++ "space.thread.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.thread-read-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/s/{space}/feed.xml": {++ "get": {++ "operationId": "get_s_space_feed_xml",++ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",++ "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",++ "tags": [++ "space.thread"++ ],++ "x-openvibe-capabilities": [++ "space.thread.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-read",++ "security": [++ {++ "openvibeToken": [++ "space.thread.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.thread-read-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/s/{space}/t/{slug}": {++ "get": {++ "operationId": "get_s_space_t_slug",++ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",++ "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",++ "tags": [++ "space.thread"++ ],++ "x-openvibe-capabilities": [++ "space.thread.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-read",++ "security": [++ {++ "openvibeToken": [++ "space.thread.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "slug",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.thread-read-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/s/feed.xml": {++ "get": {++ "operationId": "get_s_feed_xml",++ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",++ "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",++ "tags": [++ "space.thread"++ ],++ "x-openvibe-capabilities": [++ "space.thread.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "space-read",++ "security": [++ {++ "openvibeToken": [++ "space.thread.read"++ ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.thread-read-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ }++ },++ "components": {++ "schemas": {++ "common.no-body.v1": {++ "title": "NoBody",++ "description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",++ "type": "object",++ "maxProperties": 0++ },++ "errors.problem.v1": {++ "title": "Problem",++ "description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",++ "type": "object",++ "required": [++ "type",++ "title",++ "status",++ "code"++ ],++ "additionalProperties": true,++ "properties": {++ "type": {++ "type": "string",++ "format": "uri-reference"++ },++ "title": {++ "type": "string",++ "minLength": 1++ },++ "status": {++ "type": "integer",++ "minimum": 400,++ "maximum": 599++ },++ "detail": {++ "type": "string"++ },++ "instance": {++ "type": "string",++ "format": "uri-reference"++ },++ "code": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$"++ },++ "request_id": {++ "type": "string"++ },++ "trace_id": {++ "type": "string",++ "pattern": "^[0-9a-f]{32}$"++ },++ "errors": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "message"++ ],++ "properties": {++ "path": {++ "type": "string"++ },++ "message": {++ "type": "string"++ }++ }++ }++ },++ "error": {++ "type": "string",++ "description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."++ }++ }++ },++ "space.forum-post.v1": {++ "title": "SpaceForumPost",++ "description": "space.forum-post@1: one forum post (the opening post of a thread or a reply) as OpenVibe.Space's API shows it (server/forum/service.js shapePost). A deleted post keeps its place with no author, body, attachments or pastes.",++ "type": "object",++ "required": [++ "id",++ "thread_id",++ "is_opening",++ "origin",++ "author",++ "body_markdown",++ "body_html",++ "revision",++ "deleted",++ "can_edit",++ "attachments",++ "pastes"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "thread_id": {++ "type": "integer"++ },++ "is_opening": {++ "type": "boolean"++ },++ "origin": {++ "type": "string"++ },++ "author": {++ "type": [++ "object",++ "null"++ ],++ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",++ "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++ }++ }++ },++ "body_markdown": {++ "type": [++ "string",++ "null"++ ]++ },++ "body_html": {++ "type": [++ "string",++ "null"++ ]++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "deleted": {++ "type": "boolean"++ },++ "created_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "can_edit": {++ "type": "boolean"++ },++ "attachments": {++ "type": "array",++ "maxItems": 4,++ "items": {++ "type": "object",++ "required": [++ "media_id",++ "url"++ ],++ "properties": {++ "media_id": {++ "type": "string"++ },++ "url": {++ "type": "string"++ },++ "filename": {++ "type": [++ "string",++ "null"++ ]++ },++ "mime": {++ "type": "string"++ },++ "size_bytes": {++ "type": "integer"++ }++ }++ }++ },++ "pastes": {++ "type": "array",++ "maxItems": 4,++ "items": {++ "type": "object",++ "required": [++ "slug",++ "url"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "title": {++ "type": "string"++ },++ "language": {++ "type": "string"++ },++ "type": {++ "type": "string"++ },++ "url": {++ "type": "string"++ },++ "screenshot_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "lines": {++ "type": "integer"++ },++ "excerpt": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ }++ }++ }++ },++ "space.forum-read-result.v1": {++ "title": "SpaceForumReadResult",++ "description": "space.forum-read-result@1: answers of space.forum.read on OpenVibe.Space. 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": "#/components/schemas/space.forum-space.v1"++ }++ },++ "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": "#/components/schemas/space.forum-space.v1"++ }++ }++ },++ {++ "type": "string",++ "contentMediaType": "text/html",++ "description": "GET /s: the board index page."++ }++ ]++ },++ "space.forum-space.v1": {++ "title": "SpaceForumSpace",++ "description": "space.forum-space@1: one forum space as OpenVibe.Space'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"++ }++ }++ },++ "space.forum-thread.v1": {++ "title": "SpaceForumThread",++ "description": "space.forum-thread@1: one forum thread as OpenVibe.Space's /api/v1/spaces API shows it (server/forum/service.js shapeThread): where it lives, its kind and status, category, members-only gate, author, origin, pin and lock state, score, reply count, views, pages, the last post and the caller's vote.",++ "type": "object",++ "required": [++ "id",++ "space",++ "slug",++ "title",++ "url",++ "kind",++ "author",++ "origin",++ "pinned",++ "locked",++ "score",++ "reply_count",++ "pages",++ "created_at",++ "my_vote"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "space": {++ "type": "string"++ },++ "slug": {++ "type": "string"++ },++ "title": {++ "type": "string",++ "maxLength": 200++ },++ "url": {++ "type": "string",++ "description": "/s/<space>/t/<slug>"++ },++ "kind": {++ "type": "string",++ "description": "discussion, request, roadmap, …"++ },++ "status": {++ "type": [++ "string",++ "null"++ ]++ },++ "category": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "description": {++ "type": [++ "string",++ "null"++ ]++ },++ "position": {},++ "thread_count": {++ "type": "integer"++ }++ }++ },++ "members_only": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "owner": {++ "type": "string"++ },++ "owner_username": {++ "type": [++ "string",++ "null"++ ]++ },++ "join_url": {++ "type": [++ "string",++ "null"++ ]++ }++ }++ },++ "author": {++ "type": [++ "object",++ "null"++ ],++ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",++ "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++ }++ }++ },++ "origin": {++ "type": "string"++ },++ "pinned": {++ "type": "boolean"++ },++ "locked": {++ "type": "boolean"++ },++ "score": {++ "type": "integer"++ },++ "reply_count": {++ "type": "integer",++ "minimum": 0++ },++ "views": {++ "type": "integer",++ "minimum": 0++ },++ "pages": {++ "type": "integer",++ "minimum": 1++ },++ "last_post": {++ "type": [++ "object",++ "null"++ ],++ "properties": {++ "id": {++ "type": "integer"++ },++ "author": {++ "type": [++ "object",++ "null"++ ],++ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",++ "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++ }++ }++ }++ }++ },++ "crosspost_of": {},++ "last_activity_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "created_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "my_vote": {++ "enum": [++ 1,++ -1,++ 0++ ]++ }++ }++ },++ "space.post-write-request.v1": {++ "title": "SpacePostWriteRequest",++ "description": "space.post-write-request@1: bodies of the forum writes on OpenVibe.Space (space.post.write; a service writes as X-OV-Subject, or as AI with X-OV-Origin: ai). POST /api/v1/spaces/:space/threads: { title, body, members_only?, category?, attachments?, pastes? } (title 3 to 200 characters after whitespace is collapsed; body Markdown, non-blank, at most 40000 characters; body_markdown is accepted for body). POST …/threads/:slug/posts (a reply) and PUT /api/v1/posts/:id (an edit): { body, attachments?, pastes? }. POST …/threads/:slug/votes: { value: 1 | -1 | 0 }. DELETE /api/v1/posts/:id and DELETE /api/v1/spaces/:space/threads/:slug take no body. Unknown fields are ignored; refusals are problem+json (post.empty, post.too_long, thread.invalid_title, vote.invalid, thread.locked, vip.members_only, …).",++ "anyOf": [++ {++ "type": "object",++ "anyOf": [++ {++ "required": [++ "body"++ ]++ },++ {++ "required": [++ "body_markdown"++ ]++ }++ ],++ "properties": {++ "title": {++ "type": "string",++ "description": "A new thread's title."++ },++ "body": {++ "type": "string",++ "minLength": 1,++ "maxLength": 40000,++ "description": "Markdown."++ },++ "body_markdown": {++ "type": "string",++ "minLength": 1,++ "maxLength": 40000,++ "description": "Read when body is absent."++ },++ "members_only": {++ "description": "A new thread only: true gates it to the author's own VIP members; { owner: usr_… } (or the subject itself) names the creator (moderators, or the author naming themselves); null or false: open.",++ "anyOf": [++ {++ "type": [++ "boolean",++ "null"++ ]++ },++ {++ "type": "string",++ "pattern": "^usr_"++ },++ {++ "type": "object",++ "required": [++ "owner"++ ],++ "properties": {++ "owner": {++ "type": "string",++ "pattern": "^usr_"++ }++ }++ }++ ]++ },++ "category": {++ "type": "string",++ "description": "A new thread only: the slug of one of the space's categories."++ },++ "attachments": {++ "description": "At most 4 images the writer uploaded (POST /api/v1/spaces/:space/attachments) and has not used yet, by media id. People only.",++ "anyOf": [++ {++ "type": "string",++ "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "array",++ "maxItems": 4,++ "items": {++ "type": "string",++ "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ }++ ]++ },++ "pastes": {++ "description": "At most 4 shareable pastes (not private, not burn-after-read) by slug or /p/<slug> link: an array, or one string separated by spaces or commas.",++ "type": [++ "array",++ "string"++ ],++ "items": {++ "type": "string"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "value"++ ],++ "properties": {++ "value": {++ "enum": [++ 1,++ -1,++ 0,++ "1",++ "-1",++ "0"++ ],++ "description": "A thread vote; 0 takes the vote back."++ }++ }++ },++ {++ "$ref": "#/components/schemas/common.no-body.v1"++ }++ ]++ },++ "space.post-write-result.v1": {++ "title": "SpacePostWriteResult",++ "description": "space.post-write-result@1: answers of the forum writes on OpenVibe.Space (space.post.write). POST /api/v1/spaces/:space/threads → 201 { thread, post } (the thread and its opening post); POST …/threads/:slug/posts → 201 { post, page, url } (the reply, the page it lands on and its address with #post-<id>); PUT /api/v1/posts/:id → { post }; POST …/threads/:slug/votes → { thread_id, score, upvotes, downvotes, my_vote }; DELETE /api/v1/posts/:id → { ok, id } (deleting the opening post deletes the thread: { ok, id, deleted: thread }); DELETE /api/v1/spaces/:space/threads/:slug → { ok, id, deleted: thread }. Refusals are problem+json.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "thread",++ "post"++ ],++ "properties": {++ "thread": {++ "$ref": "#/components/schemas/space.forum-thread.v1"++ },++ "post": {++ "$ref": "#/components/schemas/space.forum-post.v1"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "post"++ ],++ "properties": {++ "post": {++ "$ref": "#/components/schemas/space.forum-post.v1"++ },++ "page": {++ "type": "integer",++ "minimum": 1++ },++ "url": {++ "type": "string"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "thread_id",++ "score",++ "upvotes",++ "downvotes",++ "my_vote"++ ],++ "properties": {++ "thread_id": {++ "type": "integer"++ },++ "score": {++ "type": "integer"++ },++ "upvotes": {++ "type": "integer",++ "minimum": 0++ },++ "downvotes": {++ "type": "integer",++ "minimum": 0++ },++ "my_vote": {++ "enum": [++ 1,++ -1,++ 0++ ]++ }++ }++ },++ {++ "type": "object",++ "required": [++ "ok",++ "id"++ ],++ "properties": {++ "ok": {++ "const": true++ },++ "id": {++ "type": "integer"++ },++ "deleted": {++ "const": "thread"++ }++ }++ }++ ]++ },++ "space.thread-read-result.v1": {++ "title": "SpaceThreadReadResult",++ "description": "space.thread-read-result@1: answers of space.thread.read on OpenVibe.Space. 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": "#/components/schemas/space.forum-thread.v1"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "space",++ "thread",++ "posts",++ "page",++ "pages"++ ],++ "properties": {++ "space": {++ "type": "object"++ },++ "thread": {++ "$ref": "#/components/schemas/space.forum-thread.v1"++ },++ "posts": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/space.forum-post.v1"++ }++ },++ "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."++ }++ ]++ }++ },++ "securitySchemes": {++ "openvibeToken": {++ "type": "http",++ "scheme": "bearer",++ "bearerFormat": "JWT",++ "description": "An OpenVibe Network token for this service: a person's token, an app token (client credentials, audience of the service) or a first-party service token. Its `cap` claim must include the capability the operation names."++ }++ }++ }++}@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tips API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tools API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Trade API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.VIP API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Wiki API",-- "version": "0.81.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.82.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -45238,6 +45238,435 @@ export type CommunityThreadReadResult =}| string;++/** [email protected] (owner: space) */++/**++ * space.forum-post@1: one forum post (the opening post of a thread or a reply) as OpenVibe.Space's API shows it (server/forum/service.js shapePost). A deleted post keeps its place with no author, body, attachments or pastes.++ */++export interface SpaceForumPost {++ id: number;++ thread_id: number;++ is_opening: boolean;++ origin: string;++ /**++ * The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.++ */++ 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;++ body_markdown: string | null;++ body_html: string | null;++ revision: number;++ deleted: boolean;++ created_at?: string | null;++ updated_at?: string | null;++ can_edit: boolean;++ /**++ * @maxItems 4++ */++ attachments:++ | []++ | [++ {++ media_id: string;++ url: string;++ filename?: string | null;++ mime?: string;++ size_bytes?: number;++ }++ ]++ | [++ {++ media_id: string;++ url: string;++ filename?: string | null;++ mime?: string;++ size_bytes?: number;++ },++ {++ media_id: string;++ url: string;++ filename?: string | null;++ mime?: string;++ size_bytes?: number;++ }++ ]++ | [++ {++ media_id: string;++ url: string;++ filename?: string | null;++ mime?: string;++ size_bytes?: number;++ },++ {++ media_id: string;++ url: string;++ filename?: string | null;++ mime?: string;++ size_bytes?: number;++ },++ {++ media_id: string;++ url: string;++ filename?: string | null;++ mime?: string;++ size_bytes?: number;++ }++ ]++ | [++ {++ media_id: string;++ url: string;++ filename?: string | null;++ mime?: string;++ size_bytes?: number;++ },++ {++ media_id: string;++ url: string;++ filename?: string | null;++ mime?: string;++ size_bytes?: number;++ },++ {++ media_id: string;++ url: string;++ filename?: string | null;++ mime?: string;++ size_bytes?: number;++ },++ {++ media_id: string;++ url: string;++ filename?: string | null;++ mime?: string;++ size_bytes?: number;++ }++ ];++ /**++ * @maxItems 4++ */++ pastes:++ | []++ | [++ {++ slug: string;++ title?: string;++ language?: string;++ type?: string;++ url: string;++ screenshot_url?: string | null;++ lines?: number;++ excerpt?: string | null;++ }++ ]++ | [++ {++ slug: string;++ title?: string;++ language?: string;++ type?: string;++ url: string;++ screenshot_url?: string | null;++ lines?: number;++ excerpt?: string | null;++ },++ {++ slug: string;++ title?: string;++ language?: string;++ type?: string;++ url: string;++ screenshot_url?: string | null;++ lines?: number;++ excerpt?: string | null;++ }++ ]++ | [++ {++ slug: string;++ title?: string;++ language?: string;++ type?: string;++ url: string;++ screenshot_url?: string | null;++ lines?: number;++ excerpt?: string | null;++ },++ {++ slug: string;++ title?: string;++ language?: string;++ type?: string;++ url: string;++ screenshot_url?: string | null;++ lines?: number;++ excerpt?: string | null;++ },++ {++ slug: string;++ title?: string;++ language?: string;++ type?: string;++ url: string;++ screenshot_url?: string | null;++ lines?: number;++ excerpt?: string | null;++ }++ ]++ | [++ {++ slug: string;++ title?: string;++ language?: string;++ type?: string;++ url: string;++ screenshot_url?: string | null;++ lines?: number;++ excerpt?: string | null;++ },++ {++ slug: string;++ title?: string;++ language?: string;++ type?: string;++ url: string;++ screenshot_url?: string | null;++ lines?: number;++ excerpt?: string | null;++ },++ {++ slug: string;++ title?: string;++ language?: string;++ type?: string;++ url: string;++ screenshot_url?: string | null;++ lines?: number;++ excerpt?: string | null;++ },++ {++ slug: string;++ title?: string;++ language?: string;++ type?: string;++ url: string;++ screenshot_url?: string | null;++ lines?: number;++ excerpt?: string | null;++ }++ ];++}++++/** [email protected] (owner: space) */++/**++ * space.forum-read-result@1: answers of space.forum.read on OpenVibe.Space. 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 SpaceForumReadResult =++ | {++ spaces: SpaceForumSpace[];++ groups: {++ slug: string | null;++ name: string;++ description?: string | null;++ spaces: unknown[];++ }[];++ }++ | {++ space: SpaceForumSpace;++ }++ | string;++++/** [email protected] (owner: space) */++/**++ * space.forum-space@1: one forum space as OpenVibe.Space'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 SpaceForumSpace {++ 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: space) */++/**++ * space.forum-thread@1: one forum thread as OpenVibe.Space's /api/v1/spaces API shows it (server/forum/service.js shapeThread): where it lives, its kind and status, category, members-only gate, author, origin, pin and lock state, score, reply count, views, pages, the last post and the caller's vote.++ */++export interface SpaceForumThread {++ id: number;++ space: string;++ slug: string;++ title: string;++ /**++ * /s/<space>/t/<slug>++ */++ url: string;++ /**++ * discussion, request, roadmap, …++ */++ kind: string;++ status?: string | null;++ category?: {++ slug?: string;++ name?: string;++ description?: string | null;++ position?: unknown;++ thread_count?: number;++ } | null;++ members_only?: {++ owner?: string;++ owner_username?: string | null;++ join_url?: string | null;++ } | null;++ /**++ * The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.++ */++ 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;++ origin: string;++ pinned: boolean;++ locked: boolean;++ score: number;++ reply_count: number;++ views?: number;++ pages: number;++ last_post?: {++ id?: number;++ /**++ * The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.++ */++ 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;++ } | null;++ crosspost_of?: unknown;++ last_activity_at?: string | null;++ created_at: string | null;++ my_vote: 1 | -1 | 0;++}++++/** [email protected] (owner: space) */++/**++ * space.post-write-request@1: bodies of the forum writes on OpenVibe.Space (space.post.write; a service writes as X-OV-Subject, or as AI with X-OV-Origin: ai). POST /api/v1/spaces/:space/threads: { title, body, members_only?, category?, attachments?, pastes? } (title 3 to 200 characters after whitespace is collapsed; body Markdown, non-blank, at most 40000 characters; body_markdown is accepted for body). POST …/threads/:slug/posts (a reply) and PUT /api/v1/posts/:id (an edit): { body, attachments?, pastes? }. POST …/threads/:slug/votes: { value: 1 | -1 | 0 }. DELETE /api/v1/posts/:id and DELETE /api/v1/spaces/:space/threads/:slug take no body. Unknown fields are ignored; refusals are problem+json (post.empty, post.too_long, thread.invalid_title, vote.invalid, thread.locked, vip.members_only, …).++ */++export type SpacePostWriteRequest =++ | {++ [k: string]: unknown | undefined;++ }++ | {++ /**++ * A thread vote; 0 takes the vote back.++ */++ value: 1 | -1 | 0 | "1" | "-1" | "0";++ }++ | NoBody;++++/** [email protected] (owner: space) */++/**++ * space.post-write-result@1: answers of the forum writes on OpenVibe.Space (space.post.write). POST /api/v1/spaces/:space/threads → 201 { thread, post } (the thread and its opening post); POST …/threads/:slug/posts → 201 { post, page, url } (the reply, the page it lands on and its address with #post-<id>); PUT /api/v1/posts/:id → { post }; POST …/threads/:slug/votes → { thread_id, score, upvotes, downvotes, my_vote }; DELETE /api/v1/posts/:id → { ok, id } (deleting the opening post deletes the thread: { ok, id, deleted: thread }); DELETE /api/v1/spaces/:space/threads/:slug → { ok, id, deleted: thread }. Refusals are problem+json.++ */++export type SpacePostWriteResult =++ | {++ thread: SpaceForumThread;++ post: SpaceForumPost;++ }++ | {++ post: SpaceForumPost;++ page?: number;++ url?: string;++ }++ | {++ thread_id: number;++ score: number;++ upvotes: number;++ downvotes: number;++ my_vote: 1 | -1 | 0;++ }++ | {++ ok: true;++ id: number;++ deleted?: "thread";++ };++++/** [email protected] (owner: space) */++/**++ * space.thread-read-result@1: answers of space.thread.read on OpenVibe.Space. 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 SpaceThreadReadResult =++ | {++ 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: SpaceForumThread[];++ }++ | {++ space: {};++ thread: SpaceForumThread;++ posts: SpaceForumPost[];++ 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.@@@ -2,10 +2,10 @@"id": "community.post.create","version": "1.1","owner": "community",-- "status": "active",++ "status": "deprecated","visibility": "public","sensitive": true,-- "description": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",++ "description": "DEPRECATED in favour of space.post.write (plan T10, D3): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.","outputSchema": "community.post-write-result@1","permissions": ["community:write"@@@ -2,9 +2,9 @@"id": "community.space.manage","version": "1.0","owner": "community",-- "status": "planned",++ "status": "deprecated","visibility": "internal",-- "description": "PLANNED: staff administration of spaces: create, rename, set visibility, and gate a space to one creator's VIP members. Only the members-only gate exists today (checked with community.comment.moderate plus a moderator); spaces are seeded by Community's database setup and nothing creates or edits them.",++ "description": "DEPRECATED in favour of space.forum.manage (plan T10, D3): PLANNED: staff administration of spaces: create, rename, set visibility, and gate a space to one creator's VIP members. Only the members-only gate exists today (checked with community.comment.moderate plus a moderator); spaces are seeded by Community's database setup and nothing creates or edits them.","permissions": ["community:moderate"],@@@ -2,9 +2,9 @@"id": "community.space.read","version": "1.0","owner": "community",-- "status": "active",++ "status": "deprecated","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": "DEPRECATED in favour of space.forum.read (plan T10, D3): 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": "community.space-read-result@1","permissions": ["community:read"@@@ -2,9 +2,9 @@"id": "community.thread.read","version": "1.0","owner": "community",-- "status": "active",++ "status": "deprecated","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": "DEPRECATED in favour of space.thread.read (plan T10, D3): 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": "community.thread-read-result@1","permissions": ["community:read"@@@ -4,7 +4,7 @@"owner": "community","status": "planned","visibility": "public",-- "description": "PLANNED as its own grant: up-vote, down-vote or clear a vote (value 1, -1 or 0; 0 removes it, so there is no separate vote.remove) on a thread or a comment as the acting person (roadmap §30.2 community.vote.set|remove). Locked threads refuse; 60 votes a minute per person. The routes serve production today, guarded by community.post.create (threads) and community.comment.write (comments), which keep covering them.",++ "description": "PLANNED as its own grant: up-vote, down-vote or clear a vote (value 1, -1 or 0; 0 removes it, so there is no separate vote.remove) on a thread or a comment as the acting person (roadmap §30.2 community.vote.set|remove). Locked threads refuse; 60 votes a minute per person. The routes serve production today, guarded by space.post.write (threads) and community.comment.write (comments), which keep covering them.","inputSchema": "common.entity-ref@1","permissions": ["community:vote"nnew file mode 100644@@@ -0,0 +1,17 @@++{++ "id": "download.file.read",++ "version": "1.0",++ "owner": "media-hub",++ "status": "planned",++ "visibility": "public",++ "description": "PLANNED: list and read files and folders in the caller's drive on openvibe.download, and resolve a share link (plan T10 item 12). Bytes are Media files/objects.",++ "permissions": [++ "download:read"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "download-read",++ "events": [],++ "implementedBy": []++}nnew file mode 100644@@@ -0,0 +1,18 @@++{++ "id": "download.file.upload",++ "version": "1.0",++ "owner": "media-hub",++ "status": "planned",++ "visibility": "public",++ "sensitive": true,++ "description": "PLANNED: upload a file to openvibe.download, resumable and for large files, as the acting person (account-only until T14, plan T10 D8/item 12). Bytes go to OpenVibe.Media.",++ "permissions": [++ "download:write"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "download-upload",++ "events": [],++ "implementedBy": []++}nnew file mode 100644@@@ -0,0 +1,18 @@++{++ "id": "download.share.create",++ "version": "1.0",++ "owner": "media-hub",++ "status": "planned",++ "visibility": "public",++ "sensitive": true,++ "description": "PLANNED: create a share link for a file or folder on openvibe.download (plan T10 item 12). Account-only.",++ "permissions": [++ "download:write"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "download-share",++ "events": [],++ "implementedBy": []++}nnew file mode 100644@@@ -0,0 +1,18 @@++{++ "id": "pics.album.manage",++ "version": "1.0",++ "owner": "media-hub",++ "status": "planned",++ "visibility": "public",++ "sensitive": true,++ "description": "PLANNED: create, rename and delete an album and add or remove its images on openvibe.pics (plan T10 D5). Account-only.",++ "permissions": [++ "pics:write"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "pics-write",++ "events": [],++ "implementedBy": []++}nnew file mode 100644@@@ -0,0 +1,17 @@++{++ "id": "pics.album.read",++ "version": "1.0",++ "owner": "media-hub",++ "status": "planned",++ "visibility": "public",++ "description": "PLANNED: read an album and its images on openvibe.pics (plan T10 D5).",++ "permissions": [++ "pics:read"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "pics-read",++ "events": [],++ "implementedBy": []++}nnew file mode 100644@@@ -0,0 +1,17 @@++{++ "id": "pics.image.read",++ "version": "1.0",++ "owner": "media-hub",++ "status": "planned",++ "visibility": "public",++ "description": "PLANNED: read an image on openvibe.pics: its metadata and page, and the image pastes/screenshots that become Pics (plan T10 D5). Bytes are Media objects under a pics tenant.",++ "permissions": [++ "pics:read"++ ],++ "resourceConstraints": [++ "none"++ ],++ "quotaClass": "pics-read",++ "events": [],++ "implementedBy": []++}nnew file mode 100644@@@ -0,0 +1,18 @@++{++ "id": "pics.image.upload",++ "version": "1.0",++ "owner": "media-hub",++ "status": "planned",++ "visibility": "public",++ "sensitive": true,++ "description": "PLANNED: upload an image to openvibe.pics as the acting person (account-only until T14 upload safety, plan T10 D8). Bytes go to OpenVibe.Media under a pics tenant.",++ "permissions": [++ "pics:write"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "pics-upload",++ "events": [],++ "implementedBy": []++}nnew file mode 100644@@@ -0,0 +1,19 @@++{++ "id": "space.forum.manage",++ "version": "1.0",++ "owner": "space",++ "status": "planned",++ "visibility": "internal",++ "description": "PLANNED: staff administration of forum spaces: create, rename, set visibility, and gate a space to one creator's VIP members (renamed from community.space.manage, plan T10 D3). Nothing creates or edits spaces yet; OpenVibe.Space is not built.",++ "permissions": [++ "space:moderate"++ ],++ "resourceConstraints": [++ "none"++ ],++ "quotaClass": "space-moderate",++ "events": [],++ "implementedBy": [++ "PUT /api/v1/spaces/:space/members-only"++ ]++}nnew file mode 100644@@@ -0,0 +1,23 @@++{++ "id": "space.forum.read",++ "version": "1.0",++ "owner": "space",++ "status": "active",++ "visibility": "public",++ "description": "List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Space checks space visibility itself.",++ "outputSchema": "space.forum-read-result@1",++ "permissions": [++ "space:read"++ ],++ "resourceConstraints": [++ "space_member"++ ],++ "quotaClass": "space-read",++ "events": [],++ "implementedBy": [++ "GET /api/v1/spaces",++ "GET /api/v1/spaces/:space",++ "GET /s"++ ],++ "inputSchema": "common.no-body@1"++}nnew file mode 100644@@@ -0,0 +1,26 @@++{++ "id": "space.post.write",++ "version": "1.1",++ "owner": "space",++ "status": "active",++ "visibility": "public",++ "sensitive": true,++ "description": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",++ "outputSchema": "space.post-write-result@1",++ "permissions": [++ "space:write"++ ],++ "resourceConstraints": [++ "space_member"++ ],++ "quotaClass": "space-write",++ "events": [],++ "implementedBy": [++ "POST /api/v1/spaces/:space/threads",++ "POST /api/v1/spaces/:space/threads/:slug/posts",++ "POST /api/v1/spaces/:space/threads/:slug/votes",++ "PUT|DELETE /api/v1/posts/:id",++ "DELETE /api/v1/spaces/:space/threads/:slug"++ ],++ "inputSchema": "space.post-write-request@1"++}nnew file mode 100644@@@ -0,0 +1,17 @@++{++ "id": "space.pulse.read",++ "version": "1.0",++ "owner": "space",++ "status": "planned",++ "visibility": "public",++ "description": "PLANNED: read the network Pulse activity OpenVibe.Space shows beside its forums (plan T10 D3). Community keeps the Pulse store and its own community.pulse.read/write; Space reads the same public feed for its pages. Not built.",++ "permissions": [++ "space:read"++ ],++ "resourceConstraints": [++ "none"++ ],++ "quotaClass": "space-read",++ "events": [],++ "implementedBy": []++}nnew file mode 100644@@@ -0,0 +1,26 @@++{++ "id": "space.thread.read",++ "version": "1.0",++ "owner": "space",++ "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 (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",++ "outputSchema": "space.thread-read-result@1",++ "permissions": [++ "space:read"++ ],++ "resourceConstraints": [++ "space_member"++ ],++ "quotaClass": "space-read",++ "events": [],++ "implementedBy": [++ "GET /api/v1/spaces/:space/threads",++ "GET /api/v1/spaces/:space/threads/:slug",++ "GET /s/:space",++ "GET /s/:space/t/:slug",++ "GET /s/feed.xml",++ "GET /s/:space/feed.xml"++ ],++ "inputSchema": "common.no-body@1"++}nnew file mode 100644@@@ -0,0 +1,18 @@++{++ "id": "space.thread.write",++ "version": "1.0",++ "owner": "space",++ "status": "planned",++ "visibility": "public",++ "sensitive": true,++ "description": "PLANNED: thread lifecycle as its own grant on OpenVibe.Space: create, rename, pin, lock and delete a thread (split out of community.post.create, plan T10 D3; space.post.write keeps replies, edits, votes and deletes until this lands). Account-only.",++ "permissions": [++ "space:write"++ ],++ "resourceConstraints": [++ "space_member"++ ],++ "quotaClass": "space-write",++ "events": [],++ "implementedBy": []++}nnew file mode 100644@@@ -0,0 +1,18 @@++{++ "id": "video.playlist.manage",++ "version": "1.0",++ "owner": "media-hub",++ "status": "planned",++ "visibility": "public",++ "sensitive": true,++ "description": "PLANNED: create, rename, reorder and delete a playlist on openvibe.video as the acting person (plan T10 D6). Account-only.",++ "permissions": [++ "video:write"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "video-write",++ "events": [],++ "implementedBy": []++}nnew file mode 100644@@@ -0,0 +1,17 @@++{++ "id": "video.playlist.read",++ "version": "1.0",++ "owner": "media-hub",++ "status": "planned",++ "visibility": "public",++ "description": "PLANNED: read a playlist and its ordered items on openvibe.video (a new table; playlists do not exist anywhere today, plan T10 D6).",++ "permissions": [++ "video:read"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "video-read",++ "events": [],++ "implementedBy": []++}nnew file mode 100644@@@ -0,0 +1,17 @@++{++ "id": "video.vod.read",++ "version": "1.0",++ "owner": "media-hub",++ "status": "planned",++ "visibility": "public",++ "description": "PLANNED: read one VOD or clip for a watch page on openvibe.video: the player source (a Media object), its chapters (derived from the AI moments/timeline), the WebVTT caption track (rendered from the AI transcript) and the playlists it belongs to. Bytes stay in OpenVibe.Media; the media hub reads Media, AI and Live (plan T10, D4/D6).",++ "permissions": [++ "video:read"++ ],++ "resourceConstraints": [++ "none"++ ],++ "quotaClass": "video-read",++ "events": [],++ "implementedBy": []++}nnew file mode 100644@@@ -0,0 +1,69 @@++{++ "version": "0.1.0",++ "contractRanges": {++ "openvibe-contracts": ">=0.82.0 <1.0.0"++ },++ "id": "media-hub",++ "name": "OpenVibe.MediaHub",++ "status": "placeholder",++ "repository": "OpenVibers/OpenVibe.MediaHub",++ "domains": [++ "openvibe.video",++ "openvibe.pics",++ "openvibe.download"++ ],++ "publicOrigin": "https://openvibe.video",++ "exposure": {++ "state": "placeholder",++ "publicSite": "placeholder",++ "note": "planned: no media-hub service yet; openvibe.video, openvibe.pics and openvibe.download still serve the OpenVibe.Sites placeholder. One service, three brand surfaces (plan T10, D4)"++ },++ "site": {++ "name": "Media Hub",++ "icon": "media-hub",++ "tagline": "Video, pics and downloads",++ "what": "The media-hub products in one service: video watch pages, chapters, captions and playlists (openvibe.video); image hosting and albums (openvibe.pics); share links, drive and backup (openvibe.download). Bytes stay in OpenVibe.Media.",++ "legalProfile": "hosting",++ "position": 20++ },++ "capabilities": [++ "video.vod.read",++ "video.playlist.read",++ "video.playlist.manage",++ "pics.image.read",++ "pics.image.upload",++ "pics.album.read",++ "pics.album.manage",++ "download.file.read",++ "download.file.upload",++ "download.share.create"++ ],++ "eventsProduced": [],++ "eventsConsumed": [],++ "namespacesOwned": [++ "video.*",++ "pics.*",++ "download.*"++ ],++ "lifecycle": {++ "liveness": {++ "none": "planned service: no OpenVibe.MediaHub process runs yet (plan T10 items 10-12)"++ },++ "shutdown": {++ "none": "planned service: nothing to stop yet"++ },++ "startupRecovery": {++ "none": "planned service: nothing runs to resume"++ },++ "rollback": {++ "none": "planned service: nothing is deployed to roll back"++ },++ "contracts": {++ "range": ">=0.82.0 <1.0.0"++ },++ "leases": {++ "none": "planned service: no process, no claims"++ }++ },++ "notes": "One planned media-hub service with three brand profiles (plan T10, D4). The three domains are carried in one manifest (domains is a list, as network/live/openre/games already do); the single `site` block presents the hub, and the per-brand nav profiles await the Web platform brand mechanism (ADR-039, T11). Uploads are account-only until T14 (D8). No video.*, pics.* or download.* capability is active, so none has a request/result contract yet."++}nnew file mode 100644@@@ -0,0 +1,61 @@++{++ "version": "0.1.0",++ "contractRanges": {++ "openvibe-contracts": ">=0.82.0 <1.0.0"++ },++ "id": "space",++ "name": "OpenVibe.Space",++ "status": "placeholder",++ "repository": "OpenVibers/OpenVibe.Space",++ "domains": [++ "openvibe.space"++ ],++ "publicOrigin": "https://openvibe.space",++ "exposure": {++ "state": "placeholder",++ "publicSite": "placeholder",++ "note": "planned: no OpenVibe.Space service yet; openvibe.space still serves the OpenVibe.Sites placeholder. T10 item 13 installs its own vhost"++ },++ "site": {++ "name": "Space",++ "icon": "space",++ "tagline": "Forums, boards and threads",++ "what": "OpenVibe's forums: public and members-only spaces, threads, posts, votes, categories and the Discord relay, moved off Community (plan T10, D1/D2).",++ "legalProfile": "ugc",++ "position": 19++ },++ "capabilities": [++ "space.forum.read",++ "space.forum.manage",++ "space.thread.read",++ "space.thread.write",++ "space.post.write",++ "space.pulse.read"++ ],++ "eventsProduced": [],++ "eventsConsumed": [],++ "namespacesOwned": [++ "space.*"++ ],++ "lifecycle": {++ "liveness": {++ "none": "planned service: no OpenVibe.Space process runs yet (plan T10 item 3)"++ },++ "shutdown": {++ "none": "planned service: nothing to stop yet"++ },++ "startupRecovery": {++ "none": "planned service: nothing runs to resume"++ },++ "rollback": {++ "none": "planned service: nothing is deployed to roll back"++ },++ "contracts": {++ "range": ">=0.82.0 <1.0.0"++ },++ "leases": {++ "none": "planned service: no process, no claims"++ }++ },++ "notes": "Planned. Owns the forum subsystem moved from OpenVibe.Community (spaces, threads, posts, votes, categories, reactions, moderation, chat-room attach, Discord relay) in its own database ov_space; the community.space.*/thread.read/post.create capabilities are renamed space.* and the community.space/thread/post contracts are deprecated with space.forum-*/thread-*/post-* replacements (plan T10, D1-D3). The routes these capabilities name are served by Community today and move at T10 item 4."++}@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.81.0",++ "version": "0.82.0","description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.","main": "index.js","types": "generated/typescript/index.d.ts",@@@ -440,7 +440,7 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscriptconst gaps = {'live.channel.read': 'active', 'live.stream.read': 'active', 'live.discovery.read': 'active', 'live.owner.resolve': 'planned','media.upload.create': 'planned', 'media.derivative.create': 'planned', 'media.derivative.read': 'planned', 'media.lifecycle.read': 'planned', 'media.lifecycle.transition': 'planned',-- 'community.space.read': 'active', 'community.space.manage': 'planned', 'community.thread.read': 'active', 'community.vote.set': 'planned', 'community.pulse.read': 'active',++ 'community.vote.set': 'planned', 'community.pulse.read': 'active', 'space.forum.read': 'active', 'space.forum.manage': 'planned', 'space.thread.read': 'active','search.query.run': 'active',};for (const [id, status] of Object.entries(gaps)) {@@@ -451,7 +451,7 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript}for (const id of ['media.upload.create', 'media.derivative.create']) ok(/media\.object\.upload/.test(capabilities.get(id).description) && capabilities.get(id).implementedBy.length > 0, `${id} names its routes and the id that guards them today`);ok(/media\.object\.read/.test(capabilities.get('media.lifecycle.read').description), 'media.lifecycle.read says media.object.read guards its routes today');-- ok(/community\.post\.create/.test(capabilities.get('community.vote.set').description) && /community\.comment\.write/.test(capabilities.get('community.vote.set').description), 'community.vote.set says which grants cover its routes today');++ ok(/space\.post\.write/.test(capabilities.get('community.vote.set').description) && /community\.comment\.write/.test(capabilities.get('community.vote.set').description), 'community.vote.set says which grants cover its routes today');ok(capabilities.get('live.owner.resolve').implementedBy.length === 0 && capabilities.get('live.owner.resolve').outputSchema === 'lineage.resolution@1', 'live.owner.resolve has no public route yet and answers lineage.resolution@1');ok(JSON.stringify(capabilities.get('search.query.run').implementedBy) === JSON.stringify(capabilities.get('search.query.delegate').implementedBy), 'search.query.run and search.query.delegate are the same routes, asked anonymously or for a person');const pub = services.get('publishing');
@@ -4,6 +4,28 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
@@ -4,6 +4,28 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
the notes were in the tag and commit messages (`git tag -n1`).
+## 0.82.0 — 2026-09-29
+
+**Space and the media hub: the forum moves off Community and the three new products register** (plan T10, D1–D4).
+- Two service manifests, both `status: placeholder` / `exposure.state: placeholder` (the schema has no `planned`; a
+ placeholder is "charter only", which is what these are until each serves): `manifests/services/space.json`
+ (OpenVibe.Space, `openvibe.space`) and `manifests/services/media-hub.json` (OpenVibe.MediaHub). The media hub carries
+ all three brand domains in one manifest (`domains: [openvibe.video, openvibe.pics, openvibe.download]`, the same list
+ mechanism Network/Live/OpenRe/Games already use); the per-brand nav entry awaits the ADR-039 brand mechanism (T11).
+- The forum capabilities are renamed off Community: `community.space.read` → `space.forum.read` (active),
+ `community.space.manage` → `space.forum.manage` (planned), `community.thread.read` → `space.thread.read` (active),
+ `community.post.create` → `space.post.write` (active), plus the new planned `space.thread.write` and
+ `space.pulse.read`. The four old capability manifests stay loadable with `status: deprecated` and are still listed by
+ `community` until their window closes.
+- The seven forum contracts are re-owned under `space.*` (`space.forum-space/thread/post`, `space.forum-read-result`,
+ `space.thread-read-result`, `space.post-write-request/result`) with fixtures; the `community.*` originals go
+ `deprecated` in the catalog and get a `compatibility/deprecations.json` record naming the replacement and window
+ (compat vs v0.80.0: no breaking changes). Contracts only exist for the three active capabilities.
+- The smallest honest set for the media hub, all planned: `video.vod.read`, `video.playlist.read`,
+ `video.playlist.manage`; `pics.image.read`, `pics.image.upload`, `pics.album.read`, `pics.album.manage`;
+ `download.file.read`, `download.file.upload`, `download.share.create`. No media-hub capability is active, so none has
+ a request/result contract yet.
+
## 0.81.0 — 2026-09-29
## 0.81.0 — 2026-09-29
**Opaque cursors beside the global seq** (plan T7, ADR-042, additive).
**Opaque cursors beside the global seq** (plan T7, ADR-042, additive).
@@ -1 +1,54 @@
@@ -1 +1,54 @@
-{ "_about": "Deprecated contracts: { id, replacement, since, removeAfter, reason }. A deprecated contract stays loadable until removeAfter.", "deprecations": [] }
+{
+ "_about": "Deprecated contracts: { id, replacement, since, removeAfter, reason }. A deprecated contract stays loadable until removeAfter.",
+ "deprecations": [
+ {
+ "id": "community.forum-space",
+ "replacement": "space.forum-space@1",
+ "since": "0.82.0",
+ "removeAfter": "0.84.0",
+ "reason": "the forum, its spaces/threads/posts and the community.space.*/thread.read/post.create capabilities move to OpenVibe.Space (plan T10, D1/D3)"
+ },
+ {
+ "id": "community.forum-thread",
+ "replacement": "space.forum-thread@1",
+ "since": "0.82.0",
+ "removeAfter": "0.84.0",
+ "reason": "the forum data moves to OpenVibe.Space (plan T10, D1/D3)"
+ },
+ {
+ "id": "community.forum-post",
+ "replacement": "space.forum-post@1",
+ "since": "0.82.0",
+ "removeAfter": "0.84.0",
+ "reason": "the forum data moves to OpenVibe.Space (plan T10, D1/D3)"
+ },
+ {
+ "id": "community.space-read-result",
+ "replacement": "space.forum-read-result@1",
+ "since": "0.82.0",
+ "removeAfter": "0.84.0",
+ "reason": "community.space.read is renamed space.forum.read (plan T10, D3)"
+ },
+ {
+ "id": "community.thread-read-result",
+ "replacement": "space.thread-read-result@1",
+ "since": "0.82.0",
+ "removeAfter": "0.84.0",
+ "reason": "community.thread.read is renamed space.thread.read (plan T10, D3)"
+ },
+ {
+ "id": "community.post-write-request",
+ "replacement": "space.post-write-request@1",
+ "since": "0.82.0",
+ "removeAfter": "0.84.0",
+ "reason": "community.post.create is renamed space.post.write (plan T10, D3)"
+ },
+ {
+ "id": "community.post-write-result",
+ "replacement": "space.post-write-result@1",
+ "since": "0.82.0",
+ "removeAfter": "0.84.0",
+ "reason": "community.post.create is renamed space.post.write (plan T10, D3)"
+ }
+ ]
+}
@@ -4700,7 +4700,7 @@
@@ -4700,7 +4700,7 @@
"schema": "community/post-write-request.v1.json",
"schema": "community/post-write-request.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
- "status": "active"
+ "status": "deprecated"
},
},
{
{
"id": "community.pulse-write-result",
"id": "community.pulse-write-result",
@@ -4718,7 +4718,7 @@
@@ -4718,7 +4718,7 @@
"schema": "community/forum-post.v1.json",
"schema": "community/forum-post.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
- "status": "active"
+ "status": "deprecated"
},
},
{
{
"id": "community.forum-thread",
"id": "community.forum-thread",
@@ -4727,7 +4727,7 @@
@@ -4727,7 +4727,7 @@
"schema": "community/forum-thread.v1.json",
"schema": "community/forum-thread.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
- "status": "active"
+ "status": "deprecated"
},
},
{
{
"id": "community.paste-create-result",
"id": "community.paste-create-result",
@@ -4745,7 +4745,7 @@
@@ -4745,7 +4745,7 @@
"schema": "community/post-write-result.v1.json",
"schema": "community/post-write-result.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
- "status": "active"
+ "status": "deprecated"
},
},
{
{
"id": "community.pulse-write-request",
"id": "community.pulse-write-request",
@@ -4799,7 +4799,7 @@
@@ -4799,7 +4799,7 @@
"schema": "community/forum-space.v1.json",
"schema": "community/forum-space.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
- "status": "active"
+ "status": "deprecated"
},
},
{
{
"id": "community.pulse-item",
"id": "community.pulse-item",
@@ -4826,7 +4826,7 @@
@@ -4826,7 +4826,7 @@
"schema": "community/space-read-result.v1.json",
"schema": "community/space-read-result.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
- "status": "active"
+ "status": "deprecated"
},
},
{
{
"id": "community.thread-read-result",
"id": "community.thread-read-result",
@@ -4835,6 +4835,69 @@
@@ -4835,6 +4835,69 @@
"schema": "community/thread-read-result.v1.json",
"schema": "community/thread-read-result.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
+ "status": "deprecated"
+ },
+ {
+ "id": "space.forum-post",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/forum-post.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.forum-read-result",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/forum-read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.forum-space",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/forum-space.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.forum-thread",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/forum-thread.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.post-write-request",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/post-write-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.post-write-result",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/post-write-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.thread-read-result",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/thread-read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
"status": "active"
"status": "active"
},
},
{
{
new file mode 100644
new file mode 100644
@@ -0,0 +1,190 @@
@@ -0,0 +1,190 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/forum-post.v1.json",
+ "title": "SpaceForumPost",
+ "description": "space.forum-post@1: one forum post (the opening post of a thread or a reply) as OpenVibe.Space's API shows it (server/forum/service.js shapePost). A deleted post keeps its place with no author, body, attachments or pastes.",
+ "type": "object",
+ "required": [
+ "id",
+ "thread_id",
+ "is_opening",
+ "origin",
+ "author",
+ "body_markdown",
+ "body_html",
+ "revision",
+ "deleted",
+ "can_edit",
+ "attachments",
+ "pastes"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "thread_id": {
+ "type": "integer"
+ },
+ "is_opening": {
+ "type": "boolean"
+ },
+ "origin": {
+ "type": "string"
+ },
+ "author": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
+ "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
+ }
+ }
+ },
+ "body_markdown": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "body_html": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "deleted": {
+ "type": "boolean"
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "can_edit": {
+ "type": "boolean"
+ },
+ "attachments": {
+ "type": "array",
+ "maxItems": 4,
+ "items": {
+ "type": "object",
+ "required": [
+ "media_id",
+ "url"
+ ],
+ "properties": {
+ "media_id": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "filename": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "mime": {
+ "type": "string"
+ },
+ "size_bytes": {
+ "type": "integer"
+ }
+ }
+ }
+ },
+ "pastes": {
+ "type": "array",
+ "maxItems": 4,
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "url"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string"
+ },
+ "language": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "screenshot_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lines": {
+ "type": "integer"
+ },
+ "excerpt": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ }
+ }
+ }
+}
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/space/forum-read-result.v1.json",
+ "title": "SpaceForumReadResult",
+ "description": "space.forum-read-result@1: answers of space.forum.read on OpenVibe.Space. 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,139 @@
@@ -0,0 +1,139 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/forum-space.v1.json",
+ "title": "SpaceForumSpace",
+ "description": "space.forum-space@1: one forum space as OpenVibe.Space'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,247 @@
@@ -0,0 +1,247 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/forum-thread.v1.json",
+ "title": "SpaceForumThread",
+ "description": "space.forum-thread@1: one forum thread as OpenVibe.Space's /api/v1/spaces API shows it (server/forum/service.js shapeThread): where it lives, its kind and status, category, members-only gate, author, origin, pin and lock state, score, reply count, views, pages, the last post and the caller's vote.",
+ "type": "object",
+ "required": [
+ "id",
+ "space",
+ "slug",
+ "title",
+ "url",
+ "kind",
+ "author",
+ "origin",
+ "pinned",
+ "locked",
+ "score",
+ "reply_count",
+ "pages",
+ "created_at",
+ "my_vote"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "space": {
+ "type": "string"
+ },
+ "slug": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "url": {
+ "type": "string",
+ "description": "/s/<space>/t/<slug>"
+ },
+ "kind": {
+ "type": "string",
+ "description": "discussion, request, roadmap, …"
+ },
+ "status": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "category": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "position": {},
+ "thread_count": {
+ "type": "integer"
+ }
+ }
+ },
+ "members_only": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "owner_username": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "join_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ },
+ "author": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
+ "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
+ }
+ }
+ },
+ "origin": {
+ "type": "string"
+ },
+ "pinned": {
+ "type": "boolean"
+ },
+ "locked": {
+ "type": "boolean"
+ },
+ "score": {
+ "type": "integer"
+ },
+ "reply_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "views": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "pages": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "last_post": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "author": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
+ "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
+ }
+ }
+ }
+ }
+ },
+ "crosspost_of": {},
+ "last_activity_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "my_vote": {
+ "enum": [
+ 1,
+ -1,
+ 0
+ ]
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/post-write-request.v1.json",
+ "title": "SpacePostWriteRequest",
+ "description": "space.post-write-request@1: bodies of the forum writes on OpenVibe.Space (space.post.write; a service writes as X-OV-Subject, or as AI with X-OV-Origin: ai). POST /api/v1/spaces/:space/threads: { title, body, members_only?, category?, attachments?, pastes? } (title 3 to 200 characters after whitespace is collapsed; body Markdown, non-blank, at most 40000 characters; body_markdown is accepted for body). POST …/threads/:slug/posts (a reply) and PUT /api/v1/posts/:id (an edit): { body, attachments?, pastes? }. POST …/threads/:slug/votes: { value: 1 | -1 | 0 }. DELETE /api/v1/posts/:id and DELETE /api/v1/spaces/:space/threads/:slug take no body. Unknown fields are ignored; refusals are problem+json (post.empty, post.too_long, thread.invalid_title, vote.invalid, thread.locked, vip.members_only, …).",
+ "anyOf": [
+ {
+ "type": "object",
+ "anyOf": [
+ { "required": ["body"] },
+ { "required": ["body_markdown"] }
+ ],
+ "properties": {
+ "title": { "type": "string", "description": "A new thread's title." },
+ "body": { "type": "string", "minLength": 1, "maxLength": 40000, "description": "Markdown." },
+ "body_markdown": { "type": "string", "minLength": 1, "maxLength": 40000, "description": "Read when body is absent." },
+ "members_only": {
+ "description": "A new thread only: true gates it to the author's own VIP members; { owner: usr_… } (or the subject itself) names the creator (moderators, or the author naming themselves); null or false: open.",
+ "anyOf": [
+ { "type": ["boolean", "null"] },
+ { "type": "string", "pattern": "^usr_" },
+ { "type": "object", "required": ["owner"], "properties": { "owner": { "type": "string", "pattern": "^usr_" } } }
+ ]
+ },
+ "category": { "type": "string", "description": "A new thread only: the slug of one of the space's categories." },
+ "attachments": {
+ "description": "At most 4 images the writer uploaded (POST /api/v1/spaces/:space/attachments) and has not used yet, by media id. People only.",
+ "anyOf": [
+ { "type": "string", "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$" },
+ { "type": "array", "maxItems": 4, "items": { "type": "string", "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$" } }
+ ]
+ },
+ "pastes": {
+ "description": "At most 4 shareable pastes (not private, not burn-after-read) by slug or /p/<slug> link: an array, or one string separated by spaces or commas.",
+ "type": ["array", "string"],
+ "items": { "type": "string" }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": ["value"],
+ "properties": {
+ "value": { "enum": [1, -1, 0, "1", "-1", "0"], "description": "A thread vote; 0 takes the vote back." }
+ }
+ },
+ { "$ref": "../common/no-body.v1.json" }
+ ]
+}
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/space/post-write-result.v1.json",
+ "title": "SpacePostWriteResult",
+ "description": "space.post-write-result@1: answers of the forum writes on OpenVibe.Space (space.post.write). POST /api/v1/spaces/:space/threads → 201 { thread, post } (the thread and its opening post); POST …/threads/:slug/posts → 201 { post, page, url } (the reply, the page it lands on and its address with #post-<id>); PUT /api/v1/posts/:id → { post }; POST …/threads/:slug/votes → { thread_id, score, upvotes, downvotes, my_vote }; DELETE /api/v1/posts/:id → { ok, id } (deleting the opening post deletes the thread: { ok, id, deleted: thread }); DELETE /api/v1/spaces/:space/threads/:slug → { ok, id, deleted: thread }. Refusals are problem+json.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "thread",
+ "post"
+ ],
+ "properties": {
+ "thread": {
+ "$ref": "forum-thread.v1.json"
+ },
+ "post": {
+ "$ref": "forum-post.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "post"
+ ],
+ "properties": {
+ "post": {
+ "$ref": "forum-post.v1.json"
+ },
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "url": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "thread_id",
+ "score",
+ "upvotes",
+ "downvotes",
+ "my_vote"
+ ],
+ "properties": {
+ "thread_id": {
+ "type": "integer"
+ },
+ "score": {
+ "type": "integer"
+ },
+ "upvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "downvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "my_vote": {
+ "enum": [
+ 1,
+ -1,
+ 0
+ ]
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "ok",
+ "id"
+ ],
+ "properties": {
+ "ok": {
+ "const": true
+ },
+ "id": {
+ "type": "integer"
+ },
+ "deleted": {
+ "const": "thread"
+ }
+ }
+ }
+ ]
+}
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/space/thread-read-result.v1.json",
+ "title": "SpaceThreadReadResult",
+ "description": "space.thread-read-result@1: answers of space.thread.read on OpenVibe.Space. 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 @@
@@ -0,0 +1 @@
+{ "id": 1, "thread_id": 1, "is_opening": false, "origin": "user", "author": null, "body_markdown": null, "body_html": null, "revision": 0, "deleted": true, "can_edit": false, "attachments": [], "pastes": [] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "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 }
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 @@
+{ "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": 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": 2 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "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 @@
+{ "body": "x", "attachments": ["med_1", "med_2"] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "value": 2 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "title": "Restreaming to three platforms at once", "body": "What bitrates do you use?", "category": "help", "pastes": ["k3JfQ2"] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "body_markdown": "6000 kbps works for me", "attachments": ["med_01JAB2C3D4E5F6G7H8J9K0MNPQ"] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "value": -1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "ok": true, "id": 311, "deleted": "post" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "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 }, "post": { "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 } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "post": { "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": false, "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 }, "page": 1, "url": "/s/streaming/t/restreaming-to-three-platforms#post-5121" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "ok": true, "id": 311, "deleted": "thread" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "thread_id": 311, "score": 3, "upvotes": 4, "downvotes": 1, "my_vote": 1 }
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 } ] }
@@ -51068,7 +51068,7 @@
@@ -51068,7 +51068,7 @@
"community.post-write-request": {
"community.post-write-request": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "active",
+ "status": "deprecated",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/post-write-request.v1.json",
"$id": "https://openvibe.network/contracts/community/post-write-request.v1.json",
@@ -51235,7 +51235,7 @@
@@ -51235,7 +51235,7 @@
"community.forum-post": {
"community.forum-post": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "active",
+ "status": "deprecated",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/forum-post.v1.json",
"$id": "https://openvibe.network/contracts/community/forum-post.v1.json",
@@ -51430,7 +51430,7 @@
@@ -51430,7 +51430,7 @@
"community.forum-thread": {
"community.forum-thread": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "active",
+ "status": "deprecated",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/forum-thread.v1.json",
"$id": "https://openvibe.network/contracts/community/forum-thread.v1.json",
@@ -51715,7 +51715,7 @@
@@ -51715,7 +51715,7 @@
"community.post-write-result": {
"community.post-write-result": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "active",
+ "status": "deprecated",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/post-write-result.v1.json",
"$id": "https://openvibe.network/contracts/community/post-write-result.v1.json",
@@ -52304,7 +52304,7 @@
@@ -52304,7 +52304,7 @@
"community.forum-space": {
"community.forum-space": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "active",
+ "status": "deprecated",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/forum-space.v1.json",
"$id": "https://openvibe.network/contracts/community/forum-space.v1.json",
@@ -52606,7 +52606,7 @@
@@ -52606,7 +52606,7 @@
"community.space-read-result": {
"community.space-read-result": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "active",
+ "status": "deprecated",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/space-read-result.v1.json",
"$id": "https://openvibe.network/contracts/community/space-read-result.v1.json",
@@ -52681,7 +52681,7 @@
@@ -52681,7 +52681,7 @@
"community.thread-read-result": {
"community.thread-read-result": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "active",
+ "status": "deprecated",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/thread-read-result.v1.json",
"$id": "https://openvibe.network/contracts/community/thread-read-result.v1.json",
@@ -52817,6 +52817,1034 @@
@@ -52817,6 +52817,1034 @@
]
]
}
}
},
},
+ "space.forum-post": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/forum-post.v1.json",
+ "title": "SpaceForumPost",
+ "description": "space.forum-post@1: one forum post (the opening post of a thread or a reply) as OpenVibe.Space's API shows it (server/forum/service.js shapePost). A deleted post keeps its place with no author, body, attachments or pastes.",
+ "type": "object",
+ "required": [
+ "id",
+ "thread_id",
+ "is_opening",
+ "origin",
+ "author",
+ "body_markdown",
+ "body_html",
+ "revision",
+ "deleted",
+ "can_edit",
+ "attachments",
+ "pastes"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "thread_id": {
+ "type": "integer"
+ },
+ "is_opening": {
+ "type": "boolean"
+ },
+ "origin": {
+ "type": "string"
+ },
+ "author": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
+ "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
+ }
+ }
+ },
+ "body_markdown": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "body_html": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "deleted": {
+ "type": "boolean"
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "can_edit": {
+ "type": "boolean"
+ },
+ "attachments": {
+ "type": "array",
+ "maxItems": 4,
+ "items": {
+ "type": "object",
+ "required": [
+ "media_id",
+ "url"
+ ],
+ "properties": {
+ "media_id": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "filename": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "mime": {
+ "type": "string"
+ },
+ "size_bytes": {
+ "type": "integer"
+ }
+ }
+ }
+ },
+ "pastes": {
+ "type": "array",
+ "maxItems": 4,
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "url"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string"
+ },
+ "language": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "screenshot_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lines": {
+ "type": "integer"
+ },
+ "excerpt": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "space.forum-read-result": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/forum-read-result.v1.json",
+ "title": "SpaceForumReadResult",
+ "description": "space.forum-read-result@1: answers of space.forum.read on OpenVibe.Space. 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."
+ }
+ ]
+ }
+ },
+ "space.forum-space": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/forum-space.v1.json",
+ "title": "SpaceForumSpace",
+ "description": "space.forum-space@1: one forum space as OpenVibe.Space'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"
+ }
+ }
+ }
+ },
+ "space.forum-thread": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/forum-thread.v1.json",
+ "title": "SpaceForumThread",
+ "description": "space.forum-thread@1: one forum thread as OpenVibe.Space's /api/v1/spaces API shows it (server/forum/service.js shapeThread): where it lives, its kind and status, category, members-only gate, author, origin, pin and lock state, score, reply count, views, pages, the last post and the caller's vote.",
+ "type": "object",
+ "required": [
+ "id",
+ "space",
+ "slug",
+ "title",
+ "url",
+ "kind",
+ "author",
+ "origin",
+ "pinned",
+ "locked",
+ "score",
+ "reply_count",
+ "pages",
+ "created_at",
+ "my_vote"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "space": {
+ "type": "string"
+ },
+ "slug": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "url": {
+ "type": "string",
+ "description": "/s/<space>/t/<slug>"
+ },
+ "kind": {
+ "type": "string",
+ "description": "discussion, request, roadmap, …"
+ },
+ "status": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "category": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "position": {},
+ "thread_count": {
+ "type": "integer"
+ }
+ }
+ },
+ "members_only": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "owner_username": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "join_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ },
+ "author": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
+ "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
+ }
+ }
+ },
+ "origin": {
+ "type": "string"
+ },
+ "pinned": {
+ "type": "boolean"
+ },
+ "locked": {
+ "type": "boolean"
+ },
+ "score": {
+ "type": "integer"
+ },
+ "reply_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "views": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "pages": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "last_post": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "author": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
+ "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
+ }
+ }
+ }
+ }
+ },
+ "crosspost_of": {},
+ "last_activity_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "my_vote": {
+ "enum": [
+ 1,
+ -1,
+ 0
+ ]
+ }
+ }
+ }
+ },
+ "space.post-write-request": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/post-write-request.v1.json",
+ "title": "SpacePostWriteRequest",
+ "description": "space.post-write-request@1: bodies of the forum writes on OpenVibe.Space (space.post.write; a service writes as X-OV-Subject, or as AI with X-OV-Origin: ai). POST /api/v1/spaces/:space/threads: { title, body, members_only?, category?, attachments?, pastes? } (title 3 to 200 characters after whitespace is collapsed; body Markdown, non-blank, at most 40000 characters; body_markdown is accepted for body). POST …/threads/:slug/posts (a reply) and PUT /api/v1/posts/:id (an edit): { body, attachments?, pastes? }. POST …/threads/:slug/votes: { value: 1 | -1 | 0 }. DELETE /api/v1/posts/:id and DELETE /api/v1/spaces/:space/threads/:slug take no body. Unknown fields are ignored; refusals are problem+json (post.empty, post.too_long, thread.invalid_title, vote.invalid, thread.locked, vip.members_only, …).",
+ "anyOf": [
+ {
+ "type": "object",
+ "anyOf": [
+ {
+ "required": [
+ "body"
+ ]
+ },
+ {
+ "required": [
+ "body_markdown"
+ ]
+ }
+ ],
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "A new thread's title."
+ },
+ "body": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 40000,
+ "description": "Markdown."
+ },
+ "body_markdown": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 40000,
+ "description": "Read when body is absent."
+ },
+ "members_only": {
+ "description": "A new thread only: true gates it to the author's own VIP members; { owner: usr_… } (or the subject itself) names the creator (moderators, or the author naming themselves); null or false: open.",
+ "anyOf": [
+ {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ {
+ "type": "string",
+ "pattern": "^usr_"
+ },
+ {
+ "type": "object",
+ "required": [
+ "owner"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string",
+ "pattern": "^usr_"
+ }
+ }
+ }
+ ]
+ },
+ "category": {
+ "type": "string",
+ "description": "A new thread only: the slug of one of the space's categories."
+ },
+ "attachments": {
+ "description": "At most 4 images the writer uploaded (POST /api/v1/spaces/:space/attachments) and has not used yet, by media id. People only.",
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "array",
+ "maxItems": 4,
+ "items": {
+ "type": "string",
+ "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ }
+ ]
+ },
+ "pastes": {
+ "description": "At most 4 shareable pastes (not private, not burn-after-read) by slug or /p/<slug> link: an array, or one string separated by spaces or commas.",
+ "type": [
+ "array",
+ "string"
+ ],
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "value"
+ ],
+ "properties": {
+ "value": {
+ "enum": [
+ 1,
+ -1,
+ 0,
+ "1",
+ "-1",
+ "0"
+ ],
+ "description": "A thread vote; 0 takes the vote back."
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "space.post-write-result": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/post-write-result.v1.json",
+ "title": "SpacePostWriteResult",
+ "description": "space.post-write-result@1: answers of the forum writes on OpenVibe.Space (space.post.write). POST /api/v1/spaces/:space/threads → 201 { thread, post } (the thread and its opening post); POST …/threads/:slug/posts → 201 { post, page, url } (the reply, the page it lands on and its address with #post-<id>); PUT /api/v1/posts/:id → { post }; POST …/threads/:slug/votes → { thread_id, score, upvotes, downvotes, my_vote }; DELETE /api/v1/posts/:id → { ok, id } (deleting the opening post deletes the thread: { ok, id, deleted: thread }); DELETE /api/v1/spaces/:space/threads/:slug → { ok, id, deleted: thread }. Refusals are problem+json.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "thread",
+ "post"
+ ],
+ "properties": {
+ "thread": {
+ "$ref": "forum-thread.v1.json"
+ },
+ "post": {
+ "$ref": "forum-post.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "post"
+ ],
+ "properties": {
+ "post": {
+ "$ref": "forum-post.v1.json"
+ },
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "url": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "thread_id",
+ "score",
+ "upvotes",
+ "downvotes",
+ "my_vote"
+ ],
+ "properties": {
+ "thread_id": {
+ "type": "integer"
+ },
+ "score": {
+ "type": "integer"
+ },
+ "upvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "downvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "my_vote": {
+ "enum": [
+ 1,
+ -1,
+ 0
+ ]
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "ok",
+ "id"
+ ],
+ "properties": {
+ "ok": {
+ "const": true
+ },
+ "id": {
+ "type": "integer"
+ },
+ "deleted": {
+ "const": "thread"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "space.thread-read-result": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/thread-read-result.v1.json",
+ "title": "SpaceThreadReadResult",
+ "description": "space.thread-read-result@1: answers of space.thread.read on OpenVibe.Space. 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",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.AI API",
"title": "OpenVibe.AI API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Billing API",
"title": "OpenVibe.Billing API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Blog API",
"title": "OpenVibe.Blog API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Chat API",
"title": "OpenVibe.Chat API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Codes API",
"title": "OpenVibe.Codes API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Community API",
"title": "OpenVibe.Community API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -855,18 +855,15 @@
@@ -855,18 +855,15 @@
"delete": {
"delete": {
"operationId": "delete_api_v1_posts_id",
"operationId": "delete_api_v1_posts_id",
"summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
"summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
- "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.\n\n**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
+ "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.",
"tags": [
"tags": [
- "community.comment",
- "community.post"
+ "community.comment"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "community.comment.moderate",
- "community.post.create"
+ "community.comment.moderate"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "internal",
- "public"
+ "internal"
],
],
"x-openvibe-quota-class": "community-moderate",
"x-openvibe-quota-class": "community-moderate",
"security": [
"security": [
@@ -874,22 +871,10 @@
@@ -874,22 +871,10 @@
"openvibeToken": [
"openvibeToken": [
"community.comment.moderate"
"community.comment.moderate"
]
]
- },
- {
- "openvibeToken": [
- "community.post.create"
- ]
}
}
],
],
"x-openvibe-input": {
"x-openvibe-input": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/community.moderation-request.v1"
- },
- {
- "$ref": "#/components/schemas/community.post-write-request.v1"
- }
- ]
+ "$ref": "#/components/schemas/community.moderation-request.v1"
},
},
"parameters": [
"parameters": [
{
{
@@ -907,14 +892,7 @@
@@ -907,14 +892,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/community.moderation-result.v1"
- },
- {
- "$ref": "#/components/schemas/community.post-write-result.v1"
- }
- ]
+ "$ref": "#/components/schemas/community.moderation-result.v1"
}
}
}
}
}
}
@@ -934,18 +912,15 @@
@@ -934,18 +912,15 @@
"put": {
"put": {
"operationId": "put_api_v1_posts_id",
"operationId": "put_api_v1_posts_id",
"summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
"summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
- "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.\n\n**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
+ "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.",
"tags": [
"tags": [
- "community.comment",
- "community.post"
+ "community.comment"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "community.comment.moderate",
- "community.post.create"
+ "community.comment.moderate"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "internal",
- "public"
+ "internal"
],
],
"x-openvibe-quota-class": "community-moderate",
"x-openvibe-quota-class": "community-moderate",
"security": [
"security": [
@@ -953,11 +928,6 @@
@@ -953,11 +928,6 @@
"openvibeToken": [
"openvibeToken": [
"community.comment.moderate"
"community.comment.moderate"
]
]
- },
- {
- "openvibeToken": [
- "community.post.create"
- ]
}
}
],
],
"requestBody": {
"requestBody": {
@@ -965,14 +935,7 @@
@@ -965,14 +935,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/community.moderation-request.v1"
- },
- {
- "$ref": "#/components/schemas/community.post-write-request.v1"
- }
- ]
+ "$ref": "#/components/schemas/community.moderation-request.v1"
}
}
}
}
}
}
@@ -993,14 +956,7 @@
@@ -993,14 +956,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/community.moderation-result.v1"
- },
- {
- "$ref": "#/components/schemas/community.post-write-result.v1"
- }
- ]
+ "$ref": "#/components/schemas/community.moderation-result.v1"
}
}
}
}
}
}
@@ -1352,74 +1308,31 @@
@@ -1352,74 +1308,31 @@
}
}
}
}
},
},
- "/api/v1/spaces": {
- "get": {
- "operationId": "get_api_v1_spaces",
- "summary": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*).",
- "description": "**community.space.read** (public): 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.",
- "tags": [
- "community.space"
- ],
- "x-openvibe-capabilities": [
- "community.space.read"
- ],
- "x-openvibe-visibility": [
- "public"
- ],
- "x-openvibe-quota-class": "community-read",
- "security": [
- {
- "openvibeToken": [
- "community.space.read"
- ]
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.space-read-result.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/spaces/{space}": {
- "get": {
- "operationId": "get_api_v1_spaces_space",
- "summary": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*).",
- "description": "**community.space.read** (public): 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.",
+ "/api/v1/spaces/{space}/threads/{slug}": {
+ "delete": {
+ "operationId": "delete_api_v1_spaces_space_threads_slug",
+ "summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
+ "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.",
"tags": [
"tags": [
- "community.space"
+ "community.comment"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "community.space.read"
+ "community.comment.moderate"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "public"
+ "internal"
],
],
- "x-openvibe-quota-class": "community-read",
+ "x-openvibe-quota-class": "community-moderate",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "community.space.read"
+ "community.comment.moderate"
]
]
}
}
],
],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/community.moderation-request.v1"
+ },
"parameters": [
"parameters": [
{
{
"name": "space",
"name": "space",
@@ -1428,57 +1341,9 @@
@@ -1428,57 +1341,9 @@
"schema": {
"schema": {
"type": "string"
"type": "string"
}
}
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.space-read-result.v1"
- }
- }
- }
},
},
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/spaces/{space}/threads": {
- "get": {
- "operationId": "get_api_v1_spaces_space_threads",
- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",
- "description": "**community.thread.read** (public): 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.",
- "tags": [
- "community.thread"
- ],
- "x-openvibe-capabilities": [
- "community.thread.read"
- ],
- "x-openvibe-visibility": [
- "public"
- ],
- "x-openvibe-quota-class": "community-read",
- "security": [
- {
- "openvibeToken": [
- "community.thread.read"
- ]
- }
- ],
- "parameters": [
{
{
- "name": "space",
+ "name": "slug",
"in": "path",
"in": "path",
"required": true,
"required": true,
"schema": {
"schema": {
@@ -1492,7 +1357,7 @@
@@ -1492,7 +1357,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/community.thread-read-result.v1"
+ "$ref": "#/components/schemas/community.moderation-result.v1"
}
}
}
}
}
}
@@ -1508,25 +1373,27 @@
@@ -1508,25 +1373,27 @@
}
}
}
}
}
}
- },
- "post": {
- "operationId": "post_api_v1_spaces_space_threads",
- "summary": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI",
- "description": "**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
+ }
+ },
+ "/api/v1/spaces/{space}/threads/{slug}/state": {
+ "put": {
+ "operationId": "put_api_v1_spaces_space_threads_slug_state",
+ "summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
+ "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.",
"tags": [
"tags": [
- "community.post"
+ "community.comment"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "community.post.create"
+ "community.comment.moderate"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "public"
+ "internal"
],
],
- "x-openvibe-quota-class": "community-write",
+ "x-openvibe-quota-class": "community-moderate",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "community.post.create"
+ "community.comment.moderate"
]
]
}
}
],
],
@@ -1535,7 +1402,7 @@
@@ -1535,7 +1402,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/community.post-write-request.v1"
+ "$ref": "#/components/schemas/community.moderation-request.v1"
}
}
}
}
}
}
@@ -1548,6 +1415,14 @@
@@ -1548,6 +1415,14 @@
"schema": {
"schema": {
"type": "string"
"type": "string"
}
}
+ },
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
}
}
],
],
"responses": {
"responses": {
@@ -1556,7 +1431,7 @@
@@ -1556,7 +1431,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/community.post-write-result.v1"
+ "$ref": "#/components/schemas/community.moderation-result.v1"
}
}
}
}
}
}
@@ -1574,78 +1449,35 @@
@@ -1574,78 +1449,35 @@
}
}
}
}
},
},
- "/api/v1/spaces/{space}/threads/{slug}": {
- "delete": {
- "operationId": "delete_api_v1_spaces_space_threads_slug",
- "summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
- "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.\n\n**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
+ "/pulse": {
+ "get": {
+ "operationId": "get_pulse",
+ "summary": "Read the network's public activity feed (Pulse): entity reference, title, URL and origin (user, ai or system), newest first with a cursor.",
+ "description": "**community.pulse.read** (public): 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.",
"tags": [
"tags": [
- "community.comment",
- "community.post"
+ "community.pulse"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "community.comment.moderate",
- "community.post.create"
+ "community.pulse.read"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "internal",
"public"
"public"
],
],
- "x-openvibe-quota-class": "community-moderate",
+ "x-openvibe-quota-class": "community-read",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "community.comment.moderate"
- ]
- },
- {
- "openvibeToken": [
- "community.post.create"
+ "community.pulse.read"
]
]
}
}
],
],
- "x-openvibe-input": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/community.moderation-request.v1"
- },
- {
- "$ref": "#/components/schemas/community.post-write-request.v1"
- }
- ]
- },
- "parameters": [
- {
- "name": "space",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "slug",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
"responses": {
"responses": {
"2XX": {
"2XX": {
"description": "Success",
"description": "Success",
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/community.moderation-result.v1"
- },
- {
- "$ref": "#/components/schemas/community.post-write-result.v1"
- }
- ]
+ "$ref": "#/components/schemas/community.pulse-read-result.v1"
}
}
}
}
}
}
@@ -1661,1360 +1493,141 @@
@@ -1661,1360 +1493,141 @@
}
}
}
}
}
}
- },
- "get": {
- "operationId": "get_api_v1_spaces_space_threads_slug",
- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",
- "description": "**community.thread.read** (public): 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.",
- "tags": [
- "community.thread"
- ],
- "x-openvibe-capabilities": [
- "community.thread.read"
- ],
- "x-openvibe-visibility": [
- "public"
- ],
- "x-openvibe-quota-class": "community-read",
- "security": [
- {
- "openvibeToken": [
- "community.thread.read"
- ]
- }
- ],
- "parameters": [
- {
- "name": "space",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "slug",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.thread-read-result.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/spaces/{space}/threads/{slug}/posts": {
- "post": {
- "operationId": "post_api_v1_spaces_space_threads_slug_posts",
- "summary": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI",
- "description": "**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
- "tags": [
- "community.post"
- ],
- "x-openvibe-capabilities": [
- "community.post.create"
- ],
- "x-openvibe-visibility": [
- "public"
- ],
- "x-openvibe-quota-class": "community-write",
- "security": [
- {
- "openvibeToken": [
- "community.post.create"
- ]
- }
- ],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.post-write-request.v1"
- }
- }
- }
- },
- "parameters": [
- {
- "name": "space",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "slug",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.post-write-result.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/spaces/{space}/threads/{slug}/state": {
- "put": {
- "operationId": "put_api_v1_spaces_space_threads_slug_state",
- "summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
- "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.",
- "tags": [
- "community.comment"
- ],
- "x-openvibe-capabilities": [
- "community.comment.moderate"
- ],
- "x-openvibe-visibility": [
- "internal"
- ],
- "x-openvibe-quota-class": "community-moderate",
- "security": [
- {
- "openvibeToken": [
- "community.comment.moderate"
- ]
- }
- ],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.moderation-request.v1"
- }
- }
- }
- },
- "parameters": [
- {
- "name": "space",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "slug",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.moderation-result.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/spaces/{space}/threads/{slug}/votes": {
- "post": {
- "operationId": "post_api_v1_spaces_space_threads_slug_votes",
- "summary": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI",
- "description": "**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
- "tags": [
- "community.post"
- ],
- "x-openvibe-capabilities": [
- "community.post.create"
- ],
- "x-openvibe-visibility": [
- "public"
- ],
- "x-openvibe-quota-class": "community-write",
- "security": [
- {
- "openvibeToken": [
- "community.post.create"
- ]
- }
- ],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.post-write-request.v1"
- }
- }
- }
- },
- "parameters": [
- {
- "name": "space",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "slug",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.post-write-result.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/pulse": {
- "get": {
- "operationId": "get_pulse",
- "summary": "Read the network's public activity feed (Pulse): entity reference, title, URL and origin (user, ai or system), newest first with a cursor.",
- "description": "**community.pulse.read** (public): 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.",
- "tags": [
- "community.pulse"
- ],
- "x-openvibe-capabilities": [
- "community.pulse.read"
- ],
- "x-openvibe-visibility": [
- "public"
- ],
- "x-openvibe-quota-class": "community-read",
- "security": [
- {
- "openvibeToken": [
- "community.pulse.read"
- ]
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.pulse-read-result.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/s": {
- "get": {
- "operationId": "get_s",
- "summary": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*).",
- "description": "**community.space.read** (public): 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.",
- "tags": [
- "community.space"
- ],
- "x-openvibe-capabilities": [
- "community.space.read"
- ],
- "x-openvibe-visibility": [
- "public"
- ],
- "x-openvibe-quota-class": "community-read",
- "security": [
- {
- "openvibeToken": [
- "community.space.read"
- ]
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.space-read-result.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/s/{space}": {
- "get": {
- "operationId": "get_s_space",
- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",
- "description": "**community.thread.read** (public): 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.",
- "tags": [
- "community.thread"
- ],
- "x-openvibe-capabilities": [
- "community.thread.read"
- ],
- "x-openvibe-visibility": [
- "public"
- ],
- "x-openvibe-quota-class": "community-read",
- "security": [
- {
- "openvibeToken": [
- "community.thread.read"
- ]
- }
- ],
- "parameters": [
- {
- "name": "space",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.thread-read-result.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/s/{space}/feed.xml": {
- "get": {
- "operationId": "get_s_space_feed_xml",
- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",
- "description": "**community.thread.read** (public): 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.",
- "tags": [
- "community.thread"
- ],
- "x-openvibe-capabilities": [
- "community.thread.read"
- ],
- "x-openvibe-visibility": [
- "public"
- ],
- "x-openvibe-quota-class": "community-read",
- "security": [
- {
- "openvibeToken": [
- "community.thread.read"
- ]
- }
- ],
- "parameters": [
- {
- "name": "space",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.thread-read-result.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/s/{space}/t/{slug}": {
- "get": {
- "operationId": "get_s_space_t_slug",
- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",
- "description": "**community.thread.read** (public): 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.",
- "tags": [
- "community.thread"
- ],
- "x-openvibe-capabilities": [
- "community.thread.read"
- ],
- "x-openvibe-visibility": [
- "public"
- ],
- "x-openvibe-quota-class": "community-read",
- "security": [
- {
- "openvibeToken": [
- "community.thread.read"
- ]
- }
- ],
- "parameters": [
- {
- "name": "space",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "slug",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.thread-read-result.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/s/feed.xml": {
- "get": {
- "operationId": "get_s_feed_xml",
- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",
- "description": "**community.thread.read** (public): 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.",
- "tags": [
- "community.thread"
- ],
- "x-openvibe-capabilities": [
- "community.thread.read"
- ],
- "x-openvibe-visibility": [
- "public"
- ],
- "x-openvibe-quota-class": "community-read",
- "security": [
- {
- "openvibeToken": [
- "community.thread.read"
- ]
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/community.thread-read-result.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- }
- },
- "components": {
- "schemas": {
- "common.entity-ref.v1": {
- "title": "EntityRef",
- "description": "Typed reference to another service's entity. Store this instead of a foreign key; resolve a display projection from the owner.",
- "type": "object",
- "required": [
- "service",
- "type",
- "id"
- ],
- "additionalProperties": false,
- "properties": {
- "service": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9-]{1,39}$"
- },
- "type": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]{1,39}$"
- },
- "id": {
- "type": "string",
- "minLength": 1,
- "maxLength": 128
- },
- "revision": {
- "type": "integer",
- "minimum": 0
- },
- "label": {
- "type": "string",
- "maxLength": 200,
- "description": "Cached display label. Never authoritative."
- }
- }
- },
- "common.no-body.v1": {
- "title": "NoBody",
- "description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",
- "type": "object",
- "maxProperties": 0
- },
- "community.comment-thread.v1": {
- "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": "#/components/schemas/common.entity-ref.v1"
- },
- "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.v1": {
- "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": "#/components/schemas/common.entity-ref.v1"
- }
- }
- },
- {
- "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": "#/components/schemas/common.no-body.v1"
- }
- ]
- },
- "community.comment-write-result.v1": {
- "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": "#/components/schemas/community.comment-thread.v1"
- },
- "created": {
- "type": "boolean"
- }
- }
- },
- {
- "type": "object",
- "required": [
- "thread",
- "comments",
- "next_cursor",
- "viewer"
- ],
- "properties": {
- "thread": {
- "$ref": "#/components/schemas/community.comment-thread.v1"
- },
- "comments": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/community.comment.v1"
- }
- },
- "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": "#/components/schemas/community.comment.v1"
- }
- }
- },
- {
- "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.v1": {
- "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-post.v1": {
- "title": "CommunityForumPost",
- "description": "community.forum-post@1: one forum post (the opening post of a thread or a reply) as OpenVibe.Community's API shows it (server/forum/service.js shapePost). A deleted post keeps its place with no author, body, attachments or pastes.",
- "type": "object",
- "required": [
- "id",
- "thread_id",
- "is_opening",
- "origin",
- "author",
- "body_markdown",
- "body_html",
- "revision",
- "deleted",
- "can_edit",
- "attachments",
- "pastes"
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "common.entity-ref.v1": {
+ "title": "EntityRef",
+ "description": "Typed reference to another service's entity. Store this instead of a foreign key; resolve a display projection from the owner.",
+ "type": "object",
+ "required": [
+ "service",
+ "type",
+ "id"
],
],
+ "additionalProperties": false,
"properties": {
"properties": {
- "id": {
- "type": "integer"
- },
- "thread_id": {
- "type": "integer"
- },
- "is_opening": {
- "type": "boolean"
- },
- "origin": {
- "type": "string"
- },
- "author": {
- "type": [
- "object",
- "null"
- ],
- "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
- "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
- }
- }
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
},
},
- "body_markdown": {
- "type": [
- "string",
- "null"
- ]
+ "type": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]{1,39}$"
},
},
- "body_html": {
- "type": [
- "string",
- "null"
- ]
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128
},
},
"revision": {
"revision": {
"type": "integer",
"type": "integer",
- "minimum": 1
- },
- "deleted": {
- "type": "boolean"
- },
- "created_at": {
- "type": [
- "string",
- "null"
- ],
- "format": "date-time"
- },
- "updated_at": {
- "type": [
- "string",
- "null"
- ],
- "format": "date-time"
- },
- "can_edit": {
- "type": "boolean"
- },
- "attachments": {
- "type": "array",
- "maxItems": 4,
- "items": {
- "type": "object",
- "required": [
- "media_id",
- "url"
- ],
- "properties": {
- "media_id": {
- "type": "string"
- },
- "url": {
- "type": "string"
- },
- "filename": {
- "type": [
- "string",
- "null"
- ]
- },
- "mime": {
- "type": "string"
- },
- "size_bytes": {
- "type": "integer"
- }
- }
- }
+ "minimum": 0
},
},
- "pastes": {
- "type": "array",
- "maxItems": 4,
- "items": {
- "type": "object",
- "required": [
- "slug",
- "url"
- ],
- "properties": {
- "slug": {
- "type": "string"
- },
- "title": {
- "type": "string"
- },
- "language": {
- "type": "string"
- },
- "type": {
- "type": "string"
- },
- "url": {
- "type": "string"
- },
- "screenshot_url": {
- "type": [
- "string",
- "null"
- ]
- },
- "lines": {
- "type": "integer"
- },
- "excerpt": {
- "type": [
- "string",
- "null"
- ]
- }
- }
- }
+ "label": {
+ "type": "string",
+ "maxLength": 200,
+ "description": "Cached display label. Never authoritative."
}
}
}
}
},
},
- "community.forum-space.v1": {
- "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.",
+ "common.no-body.v1": {
+ "title": "NoBody",
+ "description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",
+ "type": "object",
+ "maxProperties": 0
+ },
+ "community.comment-thread.v1": {
+ "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",
"type": "object",
"required": [
"required": [
- "slug",
- "name",
+ "id",
+ "access_id",
+ "ref",
"visibility",
"visibility",
- "url",
- "thread_kind",
- "statuses",
- "style",
- "votes",
- "reactions",
- "group",
- "parent",
- "members_only"
+ "comment_count"
],
],
"properties": {
"properties": {
- "slug": {
- "type": "string"
- },
- "name": {
- "type": "string"
- },
- "description": {
+ "id": {
"type": [
"type": [
- "string",
- "null"
- ]
- },
- "visibility": {
- "enum": [
- "public",
- "members",
- "staff"
+ "integer",
+ "string"
]
]
},
},
- "url": {
- "type": "string"
- },
- "thread_kind": {
- "type": "string"
+ "access_id": {
+ "type": "string",
+ "pattern": "^cth_"
},
},
- "statuses": {
- "type": "array",
- "items": {
- "type": "string"
- }
+ "ref": {
+ "$ref": "#/components/schemas/common.entity-ref.v1"
},
},
- "style": {
+ "visibility": {
"enum": [
"enum": [
- "feed",
- "forum"
- ]
- },
- "votes": {
- "type": "boolean"
- },
- "reactions": {
- "type": "boolean"
- },
- "group": {
- "type": [
- "object",
- "null"
- ],
- "properties": {
- "slug": {
- "type": "string"
- },
- "name": {
- "type": "string"
- }
- }
+ "public",
+ "hidden",
+ "locked"
+ ]
},
},
- "parent": {
+ "comment_count": {
"type": [
"type": [
- "object",
+ "integer",
"null"
"null"
- ],
- "properties": {
- "slug": {
- "type": "string"
- },
- "name": {
- "type": "string"
- }
- }
- },
- "post_count": {
- "type": "integer"
- },
- "thread_count": {
- "type": "integer"
+ ]
},
},
- "last_activity_at": {
+ "created_at": {
"type": [
"type": [
"string",
"string",
"null"
"null"
],
],
"format": "date-time"
"format": "date-time"
},
},
- "members_only": {
+ "updated_at": {
"type": [
"type": [
- "object",
+ "string",
"null"
"null"
],
],
+ "format": "date-time"
+ }
+ }
+ },
+ "community.comment-write-request.v1": {
+ "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": {
"properties": {
- "owner": {
- "type": "string"
+ "ref": {
+ "$ref": "#/components/schemas/common.entity-ref.v1"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "message"
+ ],
+ "properties": {
+ "message": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 5000,
+ "pattern": "\\S"
},
},
- "owner_username": {
+ "parent_id": {
"type": [
"type": [
+ "integer",
"string",
"string",
"null"
"null"
]
]
},
},
- "join_url": {
+ "anon_name": {
"type": [
"type": [
"string",
"string",
"null"
"null"
@@ -3022,119 +1635,193 @@
@@ -3022,119 +1635,193 @@
}
}
}
}
},
},
- "last_post": {
- "type": [
- "object",
- "null"
- ]
+ {
+ "type": "object",
+ "required": [
+ "value"
+ ],
+ "properties": {
+ "value": {
+ "enum": [
+ 1,
+ -1,
+ 0,
+ "1",
+ "-1",
+ "0"
+ ]
+ }
+ }
},
},
- "children": {
- "type": "array"
+ {
+ "$ref": "#/components/schemas/common.no-body.v1"
}
}
- }
+ ]
},
},
- "community.forum-thread.v1": {
- "title": "CommunityForumThread",
- "description": "community.forum-thread@1: one forum thread as OpenVibe.Community's /api/v1/spaces API shows it (server/forum/service.js shapeThread): where it lives, its kind and status, category, members-only gate, author, origin, pin and lock state, score, reply count, views, pages, the last post and the caller's vote.",
- "type": "object",
- "required": [
- "id",
- "space",
- "slug",
- "title",
- "url",
- "kind",
- "author",
- "origin",
- "pinned",
- "locked",
- "score",
- "reply_count",
- "pages",
- "created_at",
- "my_vote"
- ],
- "properties": {
- "id": {
- "type": "integer"
- },
- "space": {
- "type": "string"
- },
- "slug": {
- "type": "string"
- },
- "title": {
- "type": "string",
- "maxLength": 200
- },
- "url": {
- "type": "string",
- "description": "/s/<space>/t/<slug>"
- },
- "kind": {
- "type": "string",
- "description": "discussion, request, roadmap, …"
- },
- "status": {
- "type": [
- "string",
- "null"
- ]
+ "community.comment-write-result.v1": {
+ "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": "#/components/schemas/community.comment-thread.v1"
+ },
+ "created": {
+ "type": "boolean"
+ }
+ }
},
},
- "category": {
- "type": [
- "object",
- "null"
+ {
+ "type": "object",
+ "required": [
+ "thread",
+ "comments",
+ "next_cursor",
+ "viewer"
],
],
"properties": {
"properties": {
- "slug": {
- "type": "string"
+ "thread": {
+ "$ref": "#/components/schemas/community.comment-thread.v1"
},
},
- "name": {
- "type": "string"
+ "comments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/community.comment.v1"
+ }
},
},
- "description": {
+ "next_cursor": {
"type": [
"type": [
"string",
"string",
"null"
"null"
]
]
},
},
- "position": {},
- "thread_count": {
+ "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": "#/components/schemas/community.comment.v1"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "ok",
+ "id"
+ ],
+ "properties": {
+ "ok": {
+ "const": true
+ },
+ "id": {
"type": "integer"
"type": "integer"
}
}
}
}
},
},
- "members_only": {
- "type": [
- "object",
- "null"
+ {
+ "type": "object",
+ "required": [
+ "comment_id",
+ "score",
+ "upvotes",
+ "downvotes",
+ "my_vote"
],
],
"properties": {
"properties": {
- "owner": {
- "type": "string"
+ "comment_id": {
+ "type": "integer"
},
},
- "owner_username": {
- "type": [
- "string",
- "null"
- ]
+ "score": {
+ "type": "integer"
},
},
- "join_url": {
- "type": [
- "string",
- "null"
+ "upvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "downvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "my_vote": {
+ "enum": [
+ 1,
+ -1,
+ 0
]
]
}
}
}
}
+ }
+ ]
+ },
+ "community.comment.v1": {
+ "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": {
"author": {
"type": [
"type": [
"object",
"object",
"null"
"null"
],
],
- "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
"properties": {
"properties": {
"subject": {
"subject": {
"type": [
"type": [
@@ -3174,107 +1861,84 @@
@@ -3174,107 +1861,84 @@
}
}
}
}
},
},
- "origin": {
- "type": "string"
+ "anon_name": {
+ "type": [
+ "string",
+ "null"
+ ]
},
},
- "pinned": {
- "type": "boolean"
+ "display_name": {
+ "type": [
+ "string",
+ "null"
+ ]
},
},
- "locked": {
+ "message": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 5000
+ },
+ "deleted": {
"type": "boolean"
"type": "boolean"
},
},
"score": {
"score": {
"type": "integer"
"type": "integer"
},
},
- "reply_count": {
+ "upvotes": {
"type": "integer",
"type": "integer",
"minimum": 0
"minimum": 0
},
},
- "views": {
+ "downvotes": {
"type": "integer",
"type": "integer",
"minimum": 0
"minimum": 0
},
},
- "pages": {
- "type": "integer",
- "minimum": 1
+ "my_vote": {
+ "enum": [
+ 1,
+ -1,
+ 0
+ ]
},
},
- "last_post": {
+ "reply_count": {
"type": [
"type": [
- "object",
+ "integer",
+ "null"
+ ]
+ },
+ "created_at": {
+ "type": [
+ "string",
"null"
"null"
],
],
- "properties": {
- "id": {
- "type": "integer"
- },
- "author": {
- "type": [
- "object",
- "null"
- ],
- "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
- "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
- }
- }
- }
- }
+ "format": "date-time"
},
},
- "crosspost_of": {},
- "last_activity_at": {
+ "updated_at": {
"type": [
"type": [
"string",
"string",
"null"
"null"
],
],
"format": "date-time"
"format": "date-time"
},
},
- "created_at": {
+ "edited_at": {
"type": [
"type": [
"string",
"string",
"null"
"null"
],
],
"format": "date-time"
"format": "date-time"
},
},
- "my_vote": {
- "enum": [
- 1,
- -1,
- 0
- ]
+ "can_edit": {
+ "type": "boolean"
+ },
+ "can_delete": {
+ "type": "boolean"
+ },
+ "replies": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
}
}
}
}
},
},
@@ -4196,283 +2860,74 @@
@@ -4196,283 +2860,74 @@
"type": "boolean"
"type": "boolean"
},
},
"ai_summary": {
"ai_summary": {
- "type": [
- "string",
- "null"
- ]
- },
- "ai_tags": {
- "type": [
- "string",
- "null"
- ],
- "description": "JSON text as the AI pass wrote it."
- },
- "ai_analyzed_at": {
- "type": [
- "string",
- "null"
- ]
- },
- "revision": {
- "type": "integer"
- },
- "url": {
- "type": "string",
- "description": "/p/<slug>"
- },
- "raw_url": {
- "type": "string",
- "description": "/p/<slug>/raw"
- },
- "created_at": {
- "type": "string"
- },
- "updated_at": {
- "type": [
- "string",
- "null"
- ]
- },
- "is_owner": {
- "type": "boolean",
- "description": "Whether the caller owns it."
- },
- "username": {
- "type": [
- "string",
- "null"
- ]
- },
- "display_name": {
- "type": [
- "string",
- "null"
- ],
- "description": "OpenVibe AI for an AI paste."
- },
- "avatar_url": {
- "type": [
- "string",
- "null"
- ]
- },
- "profile_color": {
- "type": [
- "string",
- "null"
- ]
- }
- }
- },
- "community.post-write-request.v1": {
- "title": "CommunityPostWriteRequest",
- "description": "community.post-write-request@1: bodies of the forum writes on OpenVibe.Community (community.post.create; a service writes as X-OV-Subject, or as AI with X-OV-Origin: ai). POST /api/v1/spaces/:space/threads: { title, body, members_only?, category?, attachments?, pastes? } (title 3 to 200 characters after whitespace is collapsed; body Markdown, non-blank, at most 40000 characters; body_markdown is accepted for body). POST …/threads/:slug/posts (a reply) and PUT /api/v1/posts/:id (an edit): { body, attachments?, pastes? }. POST …/threads/:slug/votes: { value: 1 | -1 | 0 }. DELETE /api/v1/posts/:id and DELETE /api/v1/spaces/:space/threads/:slug take no body. Unknown fields are ignored; refusals are problem+json (post.empty, post.too_long, thread.invalid_title, vote.invalid, thread.locked, vip.members_only, …).",
- "anyOf": [
- {
- "type": "object",
- "anyOf": [
- {
- "required": [
- "body"
- ]
- },
- {
- "required": [
- "body_markdown"
- ]
- }
- ],
- "properties": {
- "title": {
- "type": "string",
- "description": "A new thread's title."
- },
- "body": {
- "type": "string",
- "minLength": 1,
- "maxLength": 40000,
- "description": "Markdown."
- },
- "body_markdown": {
- "type": "string",
- "minLength": 1,
- "maxLength": 40000,
- "description": "Read when body is absent."
- },
- "members_only": {
- "description": "A new thread only: true gates it to the author's own VIP members; { owner: usr_… } (or the subject itself) names the creator (moderators, or the author naming themselves); null or false: open.",
- "anyOf": [
- {
- "type": [
- "boolean",
- "null"
- ]
- },
- {
- "type": "string",
- "pattern": "^usr_"
- },
- {
- "type": "object",
- "required": [
- "owner"
- ],
- "properties": {
- "owner": {
- "type": "string",
- "pattern": "^usr_"
- }
- }
- }
- ]
- },
- "category": {
- "type": "string",
- "description": "A new thread only: the slug of one of the space's categories."
- },
- "attachments": {
- "description": "At most 4 images the writer uploaded (POST /api/v1/spaces/:space/attachments) and has not used yet, by media id. People only.",
- "anyOf": [
- {
- "type": "string",
- "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- {
- "type": "array",
- "maxItems": 4,
- "items": {
- "type": "string",
- "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"
- }
- }
- ]
- },
- "pastes": {
- "description": "At most 4 shareable pastes (not private, not burn-after-read) by slug or /p/<slug> link: an array, or one string separated by spaces or commas.",
- "type": [
- "array",
- "string"
- ],
- "items": {
- "type": "string"
- }
- }
- }
- },
- {
- "type": "object",
- "required": [
- "value"
- ],
- "properties": {
- "value": {
- "enum": [
- 1,
- -1,
- 0,
- "1",
- "-1",
- "0"
- ],
- "description": "A thread vote; 0 takes the vote back."
- }
- }
+ "type": [
+ "string",
+ "null"
+ ]
},
},
- {
- "$ref": "#/components/schemas/common.no-body.v1"
- }
- ]
- },
- "community.post-write-result.v1": {
- "title": "CommunityPostWriteResult",
- "description": "community.post-write-result@1: answers of the forum writes on OpenVibe.Community (community.post.create). POST /api/v1/spaces/:space/threads → 201 { thread, post } (the thread and its opening post); POST …/threads/:slug/posts → 201 { post, page, url } (the reply, the page it lands on and its address with #post-<id>); PUT /api/v1/posts/:id → { post }; POST …/threads/:slug/votes → { thread_id, score, upvotes, downvotes, my_vote }; DELETE /api/v1/posts/:id → { ok, id } (deleting the opening post deletes the thread: { ok, id, deleted: thread }); DELETE /api/v1/spaces/:space/threads/:slug → { ok, id, deleted: thread }. Refusals are problem+json.",
- "anyOf": [
- {
- "type": "object",
- "required": [
- "thread",
- "post"
+ "ai_tags": {
+ "type": [
+ "string",
+ "null"
],
],
- "properties": {
- "thread": {
- "$ref": "#/components/schemas/community.forum-thread.v1"
- },
- "post": {
- "$ref": "#/components/schemas/community.forum-post.v1"
- }
- }
+ "description": "JSON text as the AI pass wrote it."
},
},
- {
- "type": "object",
- "required": [
- "post"
- ],
- "properties": {
- "post": {
- "$ref": "#/components/schemas/community.forum-post.v1"
- },
- "page": {
- "type": "integer",
- "minimum": 1
- },
- "url": {
- "type": "string"
- }
- }
+ "ai_analyzed_at": {
+ "type": [
+ "string",
+ "null"
+ ]
},
},
- {
- "type": "object",
- "required": [
- "thread_id",
- "score",
- "upvotes",
- "downvotes",
- "my_vote"
- ],
- "properties": {
- "thread_id": {
- "type": "integer"
- },
- "score": {
- "type": "integer"
- },
- "upvotes": {
- "type": "integer",
- "minimum": 0
- },
- "downvotes": {
- "type": "integer",
- "minimum": 0
- },
- "my_vote": {
- "enum": [
- 1,
- -1,
- 0
- ]
- }
- }
+ "revision": {
+ "type": "integer"
},
},
- {
- "type": "object",
- "required": [
- "ok",
- "id"
+ "url": {
+ "type": "string",
+ "description": "/p/<slug>"
+ },
+ "raw_url": {
+ "type": "string",
+ "description": "/p/<slug>/raw"
+ },
+ "created_at": {
+ "type": "string"
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "is_owner": {
+ "type": "boolean",
+ "description": "Whether the caller owns it."
+ },
+ "username": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "display_name": {
+ "type": [
+ "string",
+ "null"
],
],
- "properties": {
- "ok": {
- "const": true
- },
- "id": {
- "type": "integer"
- },
- "deleted": {
- "const": "thread"
- }
- }
+ "description": "OpenVibe AI for an AI paste."
+ },
+ "avatar_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "profile_color": {
+ "type": [
+ "string",
+ "null"
+ ]
}
}
- ]
+ }
},
},
"community.pulse-item.v1": {
"community.pulse-item.v1": {
"title": "CommunityPulseItem",
"title": "CommunityPulseItem",
@@ -4706,206 +3161,6 @@
@@ -4706,206 +3161,6 @@
}
}
]
]
},
},
- "community.space-read-result.v1": {
- "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": "#/components/schemas/community.forum-space.v1"
- }
- },
- "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": "#/components/schemas/community.forum-space.v1"
- }
- }
- },
- {
- "type": "string",
- "contentMediaType": "text/html",
- "description": "GET /s: the board index page."
- }
- ]
- },
- "community.thread-read-result.v1": {
- "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": "#/components/schemas/community.forum-thread.v1"
- }
- }
- }
- },
- {
- "type": "object",
- "required": [
- "space",
- "thread",
- "posts",
- "page",
- "pages"
- ],
- "properties": {
- "space": {
- "type": "object"
- },
- "thread": {
- "$ref": "#/components/schemas/community.forum-thread.v1"
- },
- "posts": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/community.forum-post.v1"
- }
- },
- "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."
- }
- ]
- },
"errors.problem.v1": {
"errors.problem.v1": {
"title": "Problem",
"title": "Problem",
"description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
"description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Coupons API",
"title": "OpenVibe.Coupons API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Deals API",
"title": "OpenVibe.Deals API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Events API",
"title": "OpenVibe.Events API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Games API",
"title": "OpenVibe.Games API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Host API",
"title": "OpenVibe.Host API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -40,8 +40,8 @@
@@ -40,8 +40,8 @@
"service": "community",
"service": "community",
"name": "OpenVibe.Community",
"name": "OpenVibe.Community",
"origin": "https://openvibe.community",
"origin": "https://openvibe.community",
- "operations": 36,
- "capabilities": 10
+ "operations": 24,
+ "capabilities": 7
},
},
{
{
"service": "coupons",
"service": "coupons",
@@ -134,6 +134,13 @@
@@ -134,6 +134,13 @@
"operations": 13,
"operations": 13,
"capabilities": 3
"capabilities": 3
},
},
+ {
+ "service": "space",
+ "name": "OpenVibe.Space",
+ "origin": "https://openvibe.space",
+ "operations": 15,
+ "capabilities": 3
+ },
{
{
"service": "tips",
"service": "tips",
"name": "OpenVibe.Tips",
"name": "OpenVibe.Tips",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Live API",
"title": "OpenVibe.Live API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Media API",
"title": "OpenVibe.Media API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Network API",
"title": "OpenVibe.Network API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.News API",
"title": "OpenVibe.News API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenRe.Stream API",
"title": "OpenRe.Stream API",
- "version": "0.81.0",
- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Reviews API",
"title": "OpenVibe.Reviews API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Search API",
"title": "OpenVibe.Search API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Sources API",
"title": "OpenVibe.Sources API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
new file mode 100644
new file mode 100644
@@ -0,0 +1,1973 @@
@@ -0,0 +1,1973 @@
+{
+ "openapi": "3.1.0",
+ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
+ "info": {
+ "title": "OpenVibe.Space API",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "license": {
+ "name": "MIT",
+ "identifier": "MIT"
+ }
+ },
+ "servers": [
+ {
+ "url": "https://openvibe.space"
+ }
+ ],
+ "paths": {
+ "/api/v1/posts/{id}": {
+ "delete": {
+ "operationId": "delete_api_v1_posts_id",
+ "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",
+ "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
+ "tags": [
+ "space.post"
+ ],
+ "x-openvibe-capabilities": [
+ "space.post.write"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.post.write"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.post-write-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.post-write-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_api_v1_posts_id",
+ "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",
+ "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
+ "tags": [
+ "space.post"
+ ],
+ "x-openvibe-capabilities": [
+ "space.post.write"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.post.write"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.post-write-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.post-write-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/spaces": {
+ "get": {
+ "operationId": "get_api_v1_spaces",
+ "summary": "List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum).",
+ "description": "**space.forum.read** (public): List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Space checks space visibility itself.",
+ "tags": [
+ "space.forum"
+ ],
+ "x-openvibe-capabilities": [
+ "space.forum.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.forum.read"
+ ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.forum-read-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/spaces/{space}": {
+ "get": {
+ "operationId": "get_api_v1_spaces_space",
+ "summary": "List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum).",
+ "description": "**space.forum.read** (public): List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Space checks space visibility itself.",
+ "tags": [
+ "space.forum"
+ ],
+ "x-openvibe-capabilities": [
+ "space.forum.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.forum.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.forum-read-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/spaces/{space}/threads": {
+ "get": {
+ "operationId": "get_api_v1_spaces_space_threads",
+ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",
+ "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",
+ "tags": [
+ "space.thread"
+ ],
+ "x-openvibe-capabilities": [
+ "space.thread.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.thread.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.thread-read-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_api_v1_spaces_space_threads",
+ "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",
+ "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
+ "tags": [
+ "space.post"
+ ],
+ "x-openvibe-capabilities": [
+ "space.post.write"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.post.write"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.post-write-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.post-write-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/spaces/{space}/threads/{slug}": {
+ "delete": {
+ "operationId": "delete_api_v1_spaces_space_threads_slug",
+ "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",
+ "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
+ "tags": [
+ "space.post"
+ ],
+ "x-openvibe-capabilities": [
+ "space.post.write"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.post.write"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.post-write-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.post-write-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "get": {
+ "operationId": "get_api_v1_spaces_space_threads_slug",
+ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",
+ "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",
+ "tags": [
+ "space.thread"
+ ],
+ "x-openvibe-capabilities": [
+ "space.thread.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.thread.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.thread-read-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/spaces/{space}/threads/{slug}/posts": {
+ "post": {
+ "operationId": "post_api_v1_spaces_space_threads_slug_posts",
+ "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",
+ "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
+ "tags": [
+ "space.post"
+ ],
+ "x-openvibe-capabilities": [
+ "space.post.write"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.post.write"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.post-write-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.post-write-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/spaces/{space}/threads/{slug}/votes": {
+ "post": {
+ "operationId": "post_api_v1_spaces_space_threads_slug_votes",
+ "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",
+ "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
+ "tags": [
+ "space.post"
+ ],
+ "x-openvibe-capabilities": [
+ "space.post.write"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.post.write"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.post-write-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.post-write-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/s": {
+ "get": {
+ "operationId": "get_s",
+ "summary": "List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum).",
+ "description": "**space.forum.read** (public): List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Space checks space visibility itself.",
+ "tags": [
+ "space.forum"
+ ],
+ "x-openvibe-capabilities": [
+ "space.forum.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.forum.read"
+ ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.forum-read-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/s/{space}": {
+ "get": {
+ "operationId": "get_s_space",
+ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",
+ "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",
+ "tags": [
+ "space.thread"
+ ],
+ "x-openvibe-capabilities": [
+ "space.thread.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.thread.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.thread-read-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/s/{space}/feed.xml": {
+ "get": {
+ "operationId": "get_s_space_feed_xml",
+ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",
+ "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",
+ "tags": [
+ "space.thread"
+ ],
+ "x-openvibe-capabilities": [
+ "space.thread.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.thread.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.thread-read-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/s/{space}/t/{slug}": {
+ "get": {
+ "operationId": "get_s_space_t_slug",
+ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",
+ "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",
+ "tags": [
+ "space.thread"
+ ],
+ "x-openvibe-capabilities": [
+ "space.thread.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.thread.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.thread-read-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/s/feed.xml": {
+ "get": {
+ "operationId": "get_s_feed_xml",
+ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",
+ "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",
+ "tags": [
+ "space.thread"
+ ],
+ "x-openvibe-capabilities": [
+ "space.thread.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "space-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.thread.read"
+ ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.thread-read-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "common.no-body.v1": {
+ "title": "NoBody",
+ "description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",
+ "type": "object",
+ "maxProperties": 0
+ },
+ "errors.problem.v1": {
+ "title": "Problem",
+ "description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
+ "type": "object",
+ "required": [
+ "type",
+ "title",
+ "status",
+ "code"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "type": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1
+ },
+ "status": {
+ "type": "integer",
+ "minimum": 400,
+ "maximum": 599
+ },
+ "detail": {
+ "type": "string"
+ },
+ "instance": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "code": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$"
+ },
+ "request_id": {
+ "type": "string"
+ },
+ "trace_id": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{32}$"
+ },
+ "errors": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "message"
+ ],
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "error": {
+ "type": "string",
+ "description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."
+ }
+ }
+ },
+ "space.forum-post.v1": {
+ "title": "SpaceForumPost",
+ "description": "space.forum-post@1: one forum post (the opening post of a thread or a reply) as OpenVibe.Space's API shows it (server/forum/service.js shapePost). A deleted post keeps its place with no author, body, attachments or pastes.",
+ "type": "object",
+ "required": [
+ "id",
+ "thread_id",
+ "is_opening",
+ "origin",
+ "author",
+ "body_markdown",
+ "body_html",
+ "revision",
+ "deleted",
+ "can_edit",
+ "attachments",
+ "pastes"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "thread_id": {
+ "type": "integer"
+ },
+ "is_opening": {
+ "type": "boolean"
+ },
+ "origin": {
+ "type": "string"
+ },
+ "author": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
+ "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
+ }
+ }
+ },
+ "body_markdown": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "body_html": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "deleted": {
+ "type": "boolean"
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "can_edit": {
+ "type": "boolean"
+ },
+ "attachments": {
+ "type": "array",
+ "maxItems": 4,
+ "items": {
+ "type": "object",
+ "required": [
+ "media_id",
+ "url"
+ ],
+ "properties": {
+ "media_id": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "filename": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "mime": {
+ "type": "string"
+ },
+ "size_bytes": {
+ "type": "integer"
+ }
+ }
+ }
+ },
+ "pastes": {
+ "type": "array",
+ "maxItems": 4,
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "url"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string"
+ },
+ "language": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "screenshot_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lines": {
+ "type": "integer"
+ },
+ "excerpt": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "space.forum-read-result.v1": {
+ "title": "SpaceForumReadResult",
+ "description": "space.forum-read-result@1: answers of space.forum.read on OpenVibe.Space. 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": "#/components/schemas/space.forum-space.v1"
+ }
+ },
+ "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": "#/components/schemas/space.forum-space.v1"
+ }
+ }
+ },
+ {
+ "type": "string",
+ "contentMediaType": "text/html",
+ "description": "GET /s: the board index page."
+ }
+ ]
+ },
+ "space.forum-space.v1": {
+ "title": "SpaceForumSpace",
+ "description": "space.forum-space@1: one forum space as OpenVibe.Space'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"
+ }
+ }
+ },
+ "space.forum-thread.v1": {
+ "title": "SpaceForumThread",
+ "description": "space.forum-thread@1: one forum thread as OpenVibe.Space's /api/v1/spaces API shows it (server/forum/service.js shapeThread): where it lives, its kind and status, category, members-only gate, author, origin, pin and lock state, score, reply count, views, pages, the last post and the caller's vote.",
+ "type": "object",
+ "required": [
+ "id",
+ "space",
+ "slug",
+ "title",
+ "url",
+ "kind",
+ "author",
+ "origin",
+ "pinned",
+ "locked",
+ "score",
+ "reply_count",
+ "pages",
+ "created_at",
+ "my_vote"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "space": {
+ "type": "string"
+ },
+ "slug": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "url": {
+ "type": "string",
+ "description": "/s/<space>/t/<slug>"
+ },
+ "kind": {
+ "type": "string",
+ "description": "discussion, request, roadmap, …"
+ },
+ "status": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "category": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "position": {},
+ "thread_count": {
+ "type": "integer"
+ }
+ }
+ },
+ "members_only": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "owner_username": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "join_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ },
+ "author": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
+ "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
+ }
+ }
+ },
+ "origin": {
+ "type": "string"
+ },
+ "pinned": {
+ "type": "boolean"
+ },
+ "locked": {
+ "type": "boolean"
+ },
+ "score": {
+ "type": "integer"
+ },
+ "reply_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "views": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "pages": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "last_post": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "author": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
+ "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
+ }
+ }
+ }
+ }
+ },
+ "crosspost_of": {},
+ "last_activity_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "created_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "my_vote": {
+ "enum": [
+ 1,
+ -1,
+ 0
+ ]
+ }
+ }
+ },
+ "space.post-write-request.v1": {
+ "title": "SpacePostWriteRequest",
+ "description": "space.post-write-request@1: bodies of the forum writes on OpenVibe.Space (space.post.write; a service writes as X-OV-Subject, or as AI with X-OV-Origin: ai). POST /api/v1/spaces/:space/threads: { title, body, members_only?, category?, attachments?, pastes? } (title 3 to 200 characters after whitespace is collapsed; body Markdown, non-blank, at most 40000 characters; body_markdown is accepted for body). POST …/threads/:slug/posts (a reply) and PUT /api/v1/posts/:id (an edit): { body, attachments?, pastes? }. POST …/threads/:slug/votes: { value: 1 | -1 | 0 }. DELETE /api/v1/posts/:id and DELETE /api/v1/spaces/:space/threads/:slug take no body. Unknown fields are ignored; refusals are problem+json (post.empty, post.too_long, thread.invalid_title, vote.invalid, thread.locked, vip.members_only, …).",
+ "anyOf": [
+ {
+ "type": "object",
+ "anyOf": [
+ {
+ "required": [
+ "body"
+ ]
+ },
+ {
+ "required": [
+ "body_markdown"
+ ]
+ }
+ ],
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "A new thread's title."
+ },
+ "body": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 40000,
+ "description": "Markdown."
+ },
+ "body_markdown": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 40000,
+ "description": "Read when body is absent."
+ },
+ "members_only": {
+ "description": "A new thread only: true gates it to the author's own VIP members; { owner: usr_… } (or the subject itself) names the creator (moderators, or the author naming themselves); null or false: open.",
+ "anyOf": [
+ {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ {
+ "type": "string",
+ "pattern": "^usr_"
+ },
+ {
+ "type": "object",
+ "required": [
+ "owner"
+ ],
+ "properties": {
+ "owner": {
+ "type": "string",
+ "pattern": "^usr_"
+ }
+ }
+ }
+ ]
+ },
+ "category": {
+ "type": "string",
+ "description": "A new thread only: the slug of one of the space's categories."
+ },
+ "attachments": {
+ "description": "At most 4 images the writer uploaded (POST /api/v1/spaces/:space/attachments) and has not used yet, by media id. People only.",
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "array",
+ "maxItems": 4,
+ "items": {
+ "type": "string",
+ "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ }
+ ]
+ },
+ "pastes": {
+ "description": "At most 4 shareable pastes (not private, not burn-after-read) by slug or /p/<slug> link: an array, or one string separated by spaces or commas.",
+ "type": [
+ "array",
+ "string"
+ ],
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "value"
+ ],
+ "properties": {
+ "value": {
+ "enum": [
+ 1,
+ -1,
+ 0,
+ "1",
+ "-1",
+ "0"
+ ],
+ "description": "A thread vote; 0 takes the vote back."
+ }
+ }
+ },
+ {
+ "$ref": "#/components/schemas/common.no-body.v1"
+ }
+ ]
+ },
+ "space.post-write-result.v1": {
+ "title": "SpacePostWriteResult",
+ "description": "space.post-write-result@1: answers of the forum writes on OpenVibe.Space (space.post.write). POST /api/v1/spaces/:space/threads → 201 { thread, post } (the thread and its opening post); POST …/threads/:slug/posts → 201 { post, page, url } (the reply, the page it lands on and its address with #post-<id>); PUT /api/v1/posts/:id → { post }; POST …/threads/:slug/votes → { thread_id, score, upvotes, downvotes, my_vote }; DELETE /api/v1/posts/:id → { ok, id } (deleting the opening post deletes the thread: { ok, id, deleted: thread }); DELETE /api/v1/spaces/:space/threads/:slug → { ok, id, deleted: thread }. Refusals are problem+json.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "thread",
+ "post"
+ ],
+ "properties": {
+ "thread": {
+ "$ref": "#/components/schemas/space.forum-thread.v1"
+ },
+ "post": {
+ "$ref": "#/components/schemas/space.forum-post.v1"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "post"
+ ],
+ "properties": {
+ "post": {
+ "$ref": "#/components/schemas/space.forum-post.v1"
+ },
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "url": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "thread_id",
+ "score",
+ "upvotes",
+ "downvotes",
+ "my_vote"
+ ],
+ "properties": {
+ "thread_id": {
+ "type": "integer"
+ },
+ "score": {
+ "type": "integer"
+ },
+ "upvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "downvotes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "my_vote": {
+ "enum": [
+ 1,
+ -1,
+ 0
+ ]
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "ok",
+ "id"
+ ],
+ "properties": {
+ "ok": {
+ "const": true
+ },
+ "id": {
+ "type": "integer"
+ },
+ "deleted": {
+ "const": "thread"
+ }
+ }
+ }
+ ]
+ },
+ "space.thread-read-result.v1": {
+ "title": "SpaceThreadReadResult",
+ "description": "space.thread-read-result@1: answers of space.thread.read on OpenVibe.Space. 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": "#/components/schemas/space.forum-thread.v1"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "space",
+ "thread",
+ "posts",
+ "page",
+ "pages"
+ ],
+ "properties": {
+ "space": {
+ "type": "object"
+ },
+ "thread": {
+ "$ref": "#/components/schemas/space.forum-thread.v1"
+ },
+ "posts": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/space.forum-post.v1"
+ }
+ },
+ "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."
+ }
+ ]
+ }
+ },
+ "securitySchemes": {
+ "openvibeToken": {
+ "type": "http",
+ "scheme": "bearer",
+ "bearerFormat": "JWT",
+ "description": "An OpenVibe Network token for this service: a person's token, an app token (client credentials, audience of the service) or a first-party service token. Its `cap` claim must include the capability the operation names."
+ }
+ }
+ }
+}
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tips API",
"title": "OpenVibe.Tips API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tools API",
"title": "OpenVibe.Tools API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Trade API",
"title": "OpenVibe.Trade API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.VIP API",
"title": "OpenVibe.VIP API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Wiki API",
"title": "OpenVibe.Wiki API",
- "version": "0.81.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.81.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.82.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.82.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -45238,6 +45238,435 @@ export type CommunityThreadReadResult =
@@ -45238,6 +45238,435 @@ export type CommunityThreadReadResult =
}
}
| string;
| string;
+/** [email protected] (owner: space) */
+/**
+ * space.forum-post@1: one forum post (the opening post of a thread or a reply) as OpenVibe.Space's API shows it (server/forum/service.js shapePost). A deleted post keeps its place with no author, body, attachments or pastes.
+ */
+export interface SpaceForumPost {
+ id: number;
+ thread_id: number;
+ is_opening: boolean;
+ origin: string;
+ /**
+ * The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.
+ */
+ 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;
+ body_markdown: string | null;
+ body_html: string | null;
+ revision: number;
+ deleted: boolean;
+ created_at?: string | null;
+ updated_at?: string | null;
+ can_edit: boolean;
+ /**
+ * @maxItems 4
+ */
+ attachments:
+ | []
+ | [
+ {
+ media_id: string;
+ url: string;
+ filename?: string | null;
+ mime?: string;
+ size_bytes?: number;
+ }
+ ]
+ | [
+ {
+ media_id: string;
+ url: string;
+ filename?: string | null;
+ mime?: string;
+ size_bytes?: number;
+ },
+ {
+ media_id: string;
+ url: string;
+ filename?: string | null;
+ mime?: string;
+ size_bytes?: number;
+ }
+ ]
+ | [
+ {
+ media_id: string;
+ url: string;
+ filename?: string | null;
+ mime?: string;
+ size_bytes?: number;
+ },
+ {
+ media_id: string;
+ url: string;
+ filename?: string | null;
+ mime?: string;
+ size_bytes?: number;
+ },
+ {
+ media_id: string;
+ url: string;
+ filename?: string | null;
+ mime?: string;
+ size_bytes?: number;
+ }
+ ]
+ | [
+ {
+ media_id: string;
+ url: string;
+ filename?: string | null;
+ mime?: string;
+ size_bytes?: number;
+ },
+ {
+ media_id: string;
+ url: string;
+ filename?: string | null;
+ mime?: string;
+ size_bytes?: number;
+ },
+ {
+ media_id: string;
+ url: string;
+ filename?: string | null;
+ mime?: string;
+ size_bytes?: number;
+ },
+ {
+ media_id: string;
+ url: string;
+ filename?: string | null;
+ mime?: string;
+ size_bytes?: number;
+ }
+ ];
+ /**
+ * @maxItems 4
+ */
+ pastes:
+ | []
+ | [
+ {
+ slug: string;
+ title?: string;
+ language?: string;
+ type?: string;
+ url: string;
+ screenshot_url?: string | null;
+ lines?: number;
+ excerpt?: string | null;
+ }
+ ]
+ | [
+ {
+ slug: string;
+ title?: string;
+ language?: string;
+ type?: string;
+ url: string;
+ screenshot_url?: string | null;
+ lines?: number;
+ excerpt?: string | null;
+ },
+ {
+ slug: string;
+ title?: string;
+ language?: string;
+ type?: string;
+ url: string;
+ screenshot_url?: string | null;
+ lines?: number;
+ excerpt?: string | null;
+ }
+ ]
+ | [
+ {
+ slug: string;
+ title?: string;
+ language?: string;
+ type?: string;
+ url: string;
+ screenshot_url?: string | null;
+ lines?: number;
+ excerpt?: string | null;
+ },
+ {
+ slug: string;
+ title?: string;
+ language?: string;
+ type?: string;
+ url: string;
+ screenshot_url?: string | null;
+ lines?: number;
+ excerpt?: string | null;
+ },
+ {
+ slug: string;
+ title?: string;
+ language?: string;
+ type?: string;
+ url: string;
+ screenshot_url?: string | null;
+ lines?: number;
+ excerpt?: string | null;
+ }
+ ]
+ | [
+ {
+ slug: string;
+ title?: string;
+ language?: string;
+ type?: string;
+ url: string;
+ screenshot_url?: string | null;
+ lines?: number;
+ excerpt?: string | null;
+ },
+ {
+ slug: string;
+ title?: string;
+ language?: string;
+ type?: string;
+ url: string;
+ screenshot_url?: string | null;
+ lines?: number;
+ excerpt?: string | null;
+ },
+ {
+ slug: string;
+ title?: string;
+ language?: string;
+ type?: string;
+ url: string;
+ screenshot_url?: string | null;
+ lines?: number;
+ excerpt?: string | null;
+ },
+ {
+ slug: string;
+ title?: string;
+ language?: string;
+ type?: string;
+ url: string;
+ screenshot_url?: string | null;
+ lines?: number;
+ excerpt?: string | null;
+ }
+ ];
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.forum-read-result@1: answers of space.forum.read on OpenVibe.Space. 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 SpaceForumReadResult =
+ | {
+ spaces: SpaceForumSpace[];
+ groups: {
+ slug: string | null;
+ name: string;
+ description?: string | null;
+ spaces: unknown[];
+ }[];
+ }
+ | {
+ space: SpaceForumSpace;
+ }
+ | string;
+
+/** [email protected] (owner: space) */
+/**
+ * space.forum-space@1: one forum space as OpenVibe.Space'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 SpaceForumSpace {
+ 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: space) */
+/**
+ * space.forum-thread@1: one forum thread as OpenVibe.Space's /api/v1/spaces API shows it (server/forum/service.js shapeThread): where it lives, its kind and status, category, members-only gate, author, origin, pin and lock state, score, reply count, views, pages, the last post and the caller's vote.
+ */
+export interface SpaceForumThread {
+ id: number;
+ space: string;
+ slug: string;
+ title: string;
+ /**
+ * /s/<space>/t/<slug>
+ */
+ url: string;
+ /**
+ * discussion, request, roadmap, …
+ */
+ kind: string;
+ status?: string | null;
+ category?: {
+ slug?: string;
+ name?: string;
+ description?: string | null;
+ position?: unknown;
+ thread_count?: number;
+ } | null;
+ members_only?: {
+ owner?: string;
+ owner_username?: string | null;
+ join_url?: string | null;
+ } | null;
+ /**
+ * The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.
+ */
+ 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;
+ origin: string;
+ pinned: boolean;
+ locked: boolean;
+ score: number;
+ reply_count: number;
+ views?: number;
+ pages: number;
+ last_post?: {
+ id?: number;
+ /**
+ * The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.
+ */
+ 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;
+ } | null;
+ crosspost_of?: unknown;
+ last_activity_at?: string | null;
+ created_at: string | null;
+ my_vote: 1 | -1 | 0;
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.post-write-request@1: bodies of the forum writes on OpenVibe.Space (space.post.write; a service writes as X-OV-Subject, or as AI with X-OV-Origin: ai). POST /api/v1/spaces/:space/threads: { title, body, members_only?, category?, attachments?, pastes? } (title 3 to 200 characters after whitespace is collapsed; body Markdown, non-blank, at most 40000 characters; body_markdown is accepted for body). POST …/threads/:slug/posts (a reply) and PUT /api/v1/posts/:id (an edit): { body, attachments?, pastes? }. POST …/threads/:slug/votes: { value: 1 | -1 | 0 }. DELETE /api/v1/posts/:id and DELETE /api/v1/spaces/:space/threads/:slug take no body. Unknown fields are ignored; refusals are problem+json (post.empty, post.too_long, thread.invalid_title, vote.invalid, thread.locked, vip.members_only, …).
+ */
+export type SpacePostWriteRequest =
+ | {
+ [k: string]: unknown | undefined;
+ }
+ | {
+ /**
+ * A thread vote; 0 takes the vote back.
+ */
+ value: 1 | -1 | 0 | "1" | "-1" | "0";
+ }
+ | NoBody;
+
+/** [email protected] (owner: space) */
+/**
+ * space.post-write-result@1: answers of the forum writes on OpenVibe.Space (space.post.write). POST /api/v1/spaces/:space/threads → 201 { thread, post } (the thread and its opening post); POST …/threads/:slug/posts → 201 { post, page, url } (the reply, the page it lands on and its address with #post-<id>); PUT /api/v1/posts/:id → { post }; POST …/threads/:slug/votes → { thread_id, score, upvotes, downvotes, my_vote }; DELETE /api/v1/posts/:id → { ok, id } (deleting the opening post deletes the thread: { ok, id, deleted: thread }); DELETE /api/v1/spaces/:space/threads/:slug → { ok, id, deleted: thread }. Refusals are problem+json.
+ */
+export type SpacePostWriteResult =
+ | {
+ thread: SpaceForumThread;
+ post: SpaceForumPost;
+ }
+ | {
+ post: SpaceForumPost;
+ page?: number;
+ url?: string;
+ }
+ | {
+ thread_id: number;
+ score: number;
+ upvotes: number;
+ downvotes: number;
+ my_vote: 1 | -1 | 0;
+ }
+ | {
+ ok: true;
+ id: number;
+ deleted?: "thread";
+ };
+
+/** [email protected] (owner: space) */
+/**
+ * space.thread-read-result@1: answers of space.thread.read on OpenVibe.Space. 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 SpaceThreadReadResult =
+ | {
+ 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: SpaceForumThread[];
+ }
+ | {
+ space: {};
+ thread: SpaceForumThread;
+ posts: SpaceForumPost[];
+ 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.
@@ -2,10 +2,10 @@
@@ -2,10 +2,10 @@
"id": "community.post.create",
"id": "community.post.create",
"version": "1.1",
"version": "1.1",
"owner": "community",
"owner": "community",
- "status": "active",
+ "status": "deprecated",
"visibility": "public",
"visibility": "public",
"sensitive": true,
"sensitive": true,
- "description": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
+ "description": "DEPRECATED in favour of space.post.write (plan T10, D3): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
"outputSchema": "community.post-write-result@1",
"outputSchema": "community.post-write-result@1",
"permissions": [
"permissions": [
"community:write"
"community:write"
@@ -2,9 +2,9 @@
@@ -2,9 +2,9 @@
"id": "community.space.manage",
"id": "community.space.manage",
"version": "1.0",
"version": "1.0",
"owner": "community",
"owner": "community",
- "status": "planned",
+ "status": "deprecated",
"visibility": "internal",
"visibility": "internal",
- "description": "PLANNED: staff administration of spaces: create, rename, set visibility, and gate a space to one creator's VIP members. Only the members-only gate exists today (checked with community.comment.moderate plus a moderator); spaces are seeded by Community's database setup and nothing creates or edits them.",
+ "description": "DEPRECATED in favour of space.forum.manage (plan T10, D3): PLANNED: staff administration of spaces: create, rename, set visibility, and gate a space to one creator's VIP members. Only the members-only gate exists today (checked with community.comment.moderate plus a moderator); spaces are seeded by Community's database setup and nothing creates or edits them.",
"permissions": [
"permissions": [
"community:moderate"
"community:moderate"
],
],
@@ -2,9 +2,9 @@
@@ -2,9 +2,9 @@
"id": "community.space.read",
"id": "community.space.read",
"version": "1.0",
"version": "1.0",
"owner": "community",
"owner": "community",
- "status": "active",
+ "status": "deprecated",
"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": "DEPRECATED in favour of space.forum.read (plan T10, D3): 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": "community.space-read-result@1",
"outputSchema": "community.space-read-result@1",
"permissions": [
"permissions": [
"community:read"
"community:read"
@@ -2,9 +2,9 @@
@@ -2,9 +2,9 @@
"id": "community.thread.read",
"id": "community.thread.read",
"version": "1.0",
"version": "1.0",
"owner": "community",
"owner": "community",
- "status": "active",
+ "status": "deprecated",
"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": "DEPRECATED in favour of space.thread.read (plan T10, D3): 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": "community.thread-read-result@1",
"outputSchema": "community.thread-read-result@1",
"permissions": [
"permissions": [
"community:read"
"community:read"
@@ -4,7 +4,7 @@
@@ -4,7 +4,7 @@
"owner": "community",
"owner": "community",
"status": "planned",
"status": "planned",
"visibility": "public",
"visibility": "public",
- "description": "PLANNED as its own grant: up-vote, down-vote or clear a vote (value 1, -1 or 0; 0 removes it, so there is no separate vote.remove) on a thread or a comment as the acting person (roadmap §30.2 community.vote.set|remove). Locked threads refuse; 60 votes a minute per person. The routes serve production today, guarded by community.post.create (threads) and community.comment.write (comments), which keep covering them.",
+ "description": "PLANNED as its own grant: up-vote, down-vote or clear a vote (value 1, -1 or 0; 0 removes it, so there is no separate vote.remove) on a thread or a comment as the acting person (roadmap §30.2 community.vote.set|remove). Locked threads refuse; 60 votes a minute per person. The routes serve production today, guarded by space.post.write (threads) and community.comment.write (comments), which keep covering them.",
"inputSchema": "common.entity-ref@1",
"inputSchema": "common.entity-ref@1",
"permissions": [
"permissions": [
"community:vote"
"community:vote"
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+{
+ "id": "download.file.read",
+ "version": "1.0",
+ "owner": "media-hub",
+ "status": "planned",
+ "visibility": "public",
+ "description": "PLANNED: list and read files and folders in the caller's drive on openvibe.download, and resolve a share link (plan T10 item 12). Bytes are Media files/objects.",
+ "permissions": [
+ "download:read"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "download-read",
+ "events": [],
+ "implementedBy": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
+{
+ "id": "download.file.upload",
+ "version": "1.0",
+ "owner": "media-hub",
+ "status": "planned",
+ "visibility": "public",
+ "sensitive": true,
+ "description": "PLANNED: upload a file to openvibe.download, resumable and for large files, as the acting person (account-only until T14, plan T10 D8/item 12). Bytes go to OpenVibe.Media.",
+ "permissions": [
+ "download:write"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "download-upload",
+ "events": [],
+ "implementedBy": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
+{
+ "id": "download.share.create",
+ "version": "1.0",
+ "owner": "media-hub",
+ "status": "planned",
+ "visibility": "public",
+ "sensitive": true,
+ "description": "PLANNED: create a share link for a file or folder on openvibe.download (plan T10 item 12). Account-only.",
+ "permissions": [
+ "download:write"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "download-share",
+ "events": [],
+ "implementedBy": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
+{
+ "id": "pics.album.manage",
+ "version": "1.0",
+ "owner": "media-hub",
+ "status": "planned",
+ "visibility": "public",
+ "sensitive": true,
+ "description": "PLANNED: create, rename and delete an album and add or remove its images on openvibe.pics (plan T10 D5). Account-only.",
+ "permissions": [
+ "pics:write"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "pics-write",
+ "events": [],
+ "implementedBy": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+{
+ "id": "pics.album.read",
+ "version": "1.0",
+ "owner": "media-hub",
+ "status": "planned",
+ "visibility": "public",
+ "description": "PLANNED: read an album and its images on openvibe.pics (plan T10 D5).",
+ "permissions": [
+ "pics:read"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "pics-read",
+ "events": [],
+ "implementedBy": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+{
+ "id": "pics.image.read",
+ "version": "1.0",
+ "owner": "media-hub",
+ "status": "planned",
+ "visibility": "public",
+ "description": "PLANNED: read an image on openvibe.pics: its metadata and page, and the image pastes/screenshots that become Pics (plan T10 D5). Bytes are Media objects under a pics tenant.",
+ "permissions": [
+ "pics:read"
+ ],
+ "resourceConstraints": [
+ "none"
+ ],
+ "quotaClass": "pics-read",
+ "events": [],
+ "implementedBy": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
+{
+ "id": "pics.image.upload",
+ "version": "1.0",
+ "owner": "media-hub",
+ "status": "planned",
+ "visibility": "public",
+ "sensitive": true,
+ "description": "PLANNED: upload an image to openvibe.pics as the acting person (account-only until T14 upload safety, plan T10 D8). Bytes go to OpenVibe.Media under a pics tenant.",
+ "permissions": [
+ "pics:write"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "pics-upload",
+ "events": [],
+ "implementedBy": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
+{
+ "id": "space.forum.manage",
+ "version": "1.0",
+ "owner": "space",
+ "status": "planned",
+ "visibility": "internal",
+ "description": "PLANNED: staff administration of forum spaces: create, rename, set visibility, and gate a space to one creator's VIP members (renamed from community.space.manage, plan T10 D3). Nothing creates or edits spaces yet; OpenVibe.Space is not built.",
+ "permissions": [
+ "space:moderate"
+ ],
+ "resourceConstraints": [
+ "none"
+ ],
+ "quotaClass": "space-moderate",
+ "events": [],
+ "implementedBy": [
+ "PUT /api/v1/spaces/:space/members-only"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
+{
+ "id": "space.forum.read",
+ "version": "1.0",
+ "owner": "space",
+ "status": "active",
+ "visibility": "public",
+ "description": "List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Space checks space visibility itself.",
+ "outputSchema": "space.forum-read-result@1",
+ "permissions": [
+ "space:read"
+ ],
+ "resourceConstraints": [
+ "space_member"
+ ],
+ "quotaClass": "space-read",
+ "events": [],
+ "implementedBy": [
+ "GET /api/v1/spaces",
+ "GET /api/v1/spaces/:space",
+ "GET /s"
+ ],
+ "inputSchema": "common.no-body@1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
+{
+ "id": "space.post.write",
+ "version": "1.1",
+ "owner": "space",
+ "status": "active",
+ "visibility": "public",
+ "sensitive": true,
+ "description": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
+ "outputSchema": "space.post-write-result@1",
+ "permissions": [
+ "space:write"
+ ],
+ "resourceConstraints": [
+ "space_member"
+ ],
+ "quotaClass": "space-write",
+ "events": [],
+ "implementedBy": [
+ "POST /api/v1/spaces/:space/threads",
+ "POST /api/v1/spaces/:space/threads/:slug/posts",
+ "POST /api/v1/spaces/:space/threads/:slug/votes",
+ "PUT|DELETE /api/v1/posts/:id",
+ "DELETE /api/v1/spaces/:space/threads/:slug"
+ ],
+ "inputSchema": "space.post-write-request@1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+{
+ "id": "space.pulse.read",
+ "version": "1.0",
+ "owner": "space",
+ "status": "planned",
+ "visibility": "public",
+ "description": "PLANNED: read the network Pulse activity OpenVibe.Space shows beside its forums (plan T10 D3). Community keeps the Pulse store and its own community.pulse.read/write; Space reads the same public feed for its pages. Not built.",
+ "permissions": [
+ "space:read"
+ ],
+ "resourceConstraints": [
+ "none"
+ ],
+ "quotaClass": "space-read",
+ "events": [],
+ "implementedBy": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
+{
+ "id": "space.thread.read",
+ "version": "1.0",
+ "owner": "space",
+ "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 (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write and space.thread.write.",
+ "outputSchema": "space.thread-read-result@1",
+ "permissions": [
+ "space:read"
+ ],
+ "resourceConstraints": [
+ "space_member"
+ ],
+ "quotaClass": "space-read",
+ "events": [],
+ "implementedBy": [
+ "GET /api/v1/spaces/:space/threads",
+ "GET /api/v1/spaces/:space/threads/:slug",
+ "GET /s/:space",
+ "GET /s/:space/t/:slug",
+ "GET /s/feed.xml",
+ "GET /s/:space/feed.xml"
+ ],
+ "inputSchema": "common.no-body@1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
+{
+ "id": "space.thread.write",
+ "version": "1.0",
+ "owner": "space",
+ "status": "planned",
+ "visibility": "public",
+ "sensitive": true,
+ "description": "PLANNED: thread lifecycle as its own grant on OpenVibe.Space: create, rename, pin, lock and delete a thread (split out of community.post.create, plan T10 D3; space.post.write keeps replies, edits, votes and deletes until this lands). Account-only.",
+ "permissions": [
+ "space:write"
+ ],
+ "resourceConstraints": [
+ "space_member"
+ ],
+ "quotaClass": "space-write",
+ "events": [],
+ "implementedBy": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
+{
+ "id": "video.playlist.manage",
+ "version": "1.0",
+ "owner": "media-hub",
+ "status": "planned",
+ "visibility": "public",
+ "sensitive": true,
+ "description": "PLANNED: create, rename, reorder and delete a playlist on openvibe.video as the acting person (plan T10 D6). Account-only.",
+ "permissions": [
+ "video:write"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "video-write",
+ "events": [],
+ "implementedBy": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+{
+ "id": "video.playlist.read",
+ "version": "1.0",
+ "owner": "media-hub",
+ "status": "planned",
+ "visibility": "public",
+ "description": "PLANNED: read a playlist and its ordered items on openvibe.video (a new table; playlists do not exist anywhere today, plan T10 D6).",
+ "permissions": [
+ "video:read"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "video-read",
+ "events": [],
+ "implementedBy": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+{
+ "id": "video.vod.read",
+ "version": "1.0",
+ "owner": "media-hub",
+ "status": "planned",
+ "visibility": "public",
+ "description": "PLANNED: read one VOD or clip for a watch page on openvibe.video: the player source (a Media object), its chapters (derived from the AI moments/timeline), the WebVTT caption track (rendered from the AI transcript) and the playlists it belongs to. Bytes stay in OpenVibe.Media; the media hub reads Media, AI and Live (plan T10, D4/D6).",
+ "permissions": [
+ "video:read"
+ ],
+ "resourceConstraints": [
+ "none"
+ ],
+ "quotaClass": "video-read",
+ "events": [],
+ "implementedBy": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,69 @@
@@ -0,0 +1,69 @@
+{
+ "version": "0.1.0",
+ "contractRanges": {
+ "openvibe-contracts": ">=0.82.0 <1.0.0"
+ },
+ "id": "media-hub",
+ "name": "OpenVibe.MediaHub",
+ "status": "placeholder",
+ "repository": "OpenVibers/OpenVibe.MediaHub",
+ "domains": [
+ "openvibe.video",
+ "openvibe.pics",
+ "openvibe.download"
+ ],
+ "publicOrigin": "https://openvibe.video",
+ "exposure": {
+ "state": "placeholder",
+ "publicSite": "placeholder",
+ "note": "planned: no media-hub service yet; openvibe.video, openvibe.pics and openvibe.download still serve the OpenVibe.Sites placeholder. One service, three brand surfaces (plan T10, D4)"
+ },
+ "site": {
+ "name": "Media Hub",
+ "icon": "media-hub",
+ "tagline": "Video, pics and downloads",
+ "what": "The media-hub products in one service: video watch pages, chapters, captions and playlists (openvibe.video); image hosting and albums (openvibe.pics); share links, drive and backup (openvibe.download). Bytes stay in OpenVibe.Media.",
+ "legalProfile": "hosting",
+ "position": 20
+ },
+ "capabilities": [
+ "video.vod.read",
+ "video.playlist.read",
+ "video.playlist.manage",
+ "pics.image.read",
+ "pics.image.upload",
+ "pics.album.read",
+ "pics.album.manage",
+ "download.file.read",
+ "download.file.upload",
+ "download.share.create"
+ ],
+ "eventsProduced": [],
+ "eventsConsumed": [],
+ "namespacesOwned": [
+ "video.*",
+ "pics.*",
+ "download.*"
+ ],
+ "lifecycle": {
+ "liveness": {
+ "none": "planned service: no OpenVibe.MediaHub process runs yet (plan T10 items 10-12)"
+ },
+ "shutdown": {
+ "none": "planned service: nothing to stop yet"
+ },
+ "startupRecovery": {
+ "none": "planned service: nothing runs to resume"
+ },
+ "rollback": {
+ "none": "planned service: nothing is deployed to roll back"
+ },
+ "contracts": {
+ "range": ">=0.82.0 <1.0.0"
+ },
+ "leases": {
+ "none": "planned service: no process, no claims"
+ }
+ },
+ "notes": "One planned media-hub service with three brand profiles (plan T10, D4). The three domains are carried in one manifest (domains is a list, as network/live/openre/games already do); the single `site` block presents the hub, and the per-brand nav profiles await the Web platform brand mechanism (ADR-039, T11). Uploads are account-only until T14 (D8). No video.*, pics.* or download.* capability is active, so none has a request/result contract yet."
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
+{
+ "version": "0.1.0",
+ "contractRanges": {
+ "openvibe-contracts": ">=0.82.0 <1.0.0"
+ },
+ "id": "space",
+ "name": "OpenVibe.Space",
+ "status": "placeholder",
+ "repository": "OpenVibers/OpenVibe.Space",
+ "domains": [
+ "openvibe.space"
+ ],
+ "publicOrigin": "https://openvibe.space",
+ "exposure": {
+ "state": "placeholder",
+ "publicSite": "placeholder",
+ "note": "planned: no OpenVibe.Space service yet; openvibe.space still serves the OpenVibe.Sites placeholder. T10 item 13 installs its own vhost"
+ },
+ "site": {
+ "name": "Space",
+ "icon": "space",
+ "tagline": "Forums, boards and threads",
+ "what": "OpenVibe's forums: public and members-only spaces, threads, posts, votes, categories and the Discord relay, moved off Community (plan T10, D1/D2).",
+ "legalProfile": "ugc",
+ "position": 19
+ },
+ "capabilities": [
+ "space.forum.read",
+ "space.forum.manage",
+ "space.thread.read",
+ "space.thread.write",
+ "space.post.write",
+ "space.pulse.read"
+ ],
+ "eventsProduced": [],
+ "eventsConsumed": [],
+ "namespacesOwned": [
+ "space.*"
+ ],
+ "lifecycle": {
+ "liveness": {
+ "none": "planned service: no OpenVibe.Space process runs yet (plan T10 item 3)"
+ },
+ "shutdown": {
+ "none": "planned service: nothing to stop yet"
+ },
+ "startupRecovery": {
+ "none": "planned service: nothing runs to resume"
+ },
+ "rollback": {
+ "none": "planned service: nothing is deployed to roll back"
+ },
+ "contracts": {
+ "range": ">=0.82.0 <1.0.0"
+ },
+ "leases": {
+ "none": "planned service: no process, no claims"
+ }
+ },
+ "notes": "Planned. Owns the forum subsystem moved from OpenVibe.Community (spaces, threads, posts, votes, categories, reactions, moderation, chat-room attach, Discord relay) in its own database ov_space; the community.space.*/thread.read/post.create capabilities are renamed space.* and the community.space/thread/post contracts are deprecated with space.forum-*/thread-*/post-* replacements (plan T10, D1-D3). The routes these capabilities name are served by Community today and move at T10 item 4."
+}
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.81.0",
+ "version": "0.82.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
"types": "generated/typescript/index.d.ts",
@@ -440,7 +440,7 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
@@ -440,7 +440,7 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
const gaps = {
const gaps = {
'live.channel.read': 'active', 'live.stream.read': 'active', 'live.discovery.read': 'active', 'live.owner.resolve': 'planned',
'live.channel.read': 'active', 'live.stream.read': 'active', 'live.discovery.read': 'active', 'live.owner.resolve': 'planned',
'media.upload.create': 'planned', 'media.derivative.create': 'planned', 'media.derivative.read': 'planned', 'media.lifecycle.read': 'planned', 'media.lifecycle.transition': 'planned',
'media.upload.create': 'planned', 'media.derivative.create': 'planned', 'media.derivative.read': 'planned', 'media.lifecycle.read': 'planned', 'media.lifecycle.transition': 'planned',
- 'community.space.read': 'active', 'community.space.manage': 'planned', 'community.thread.read': 'active', 'community.vote.set': 'planned', 'community.pulse.read': 'active',
+ 'community.vote.set': 'planned', 'community.pulse.read': 'active', 'space.forum.read': 'active', 'space.forum.manage': 'planned', 'space.thread.read': 'active',
'search.query.run': 'active',
'search.query.run': 'active',
};
};
for (const [id, status] of Object.entries(gaps)) {
for (const [id, status] of Object.entries(gaps)) {
@@ -451,7 +451,7 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
@@ -451,7 +451,7 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
}
}
for (const id of ['media.upload.create', 'media.derivative.create']) ok(/media\.object\.upload/.test(capabilities.get(id).description) && capabilities.get(id).implementedBy.length > 0, `${id} names its routes and the id that guards them today`);
for (const id of ['media.upload.create', 'media.derivative.create']) ok(/media\.object\.upload/.test(capabilities.get(id).description) && capabilities.get(id).implementedBy.length > 0, `${id} names its routes and the id that guards them today`);
ok(/media\.object\.read/.test(capabilities.get('media.lifecycle.read').description), 'media.lifecycle.read says media.object.read guards its routes today');
ok(/media\.object\.read/.test(capabilities.get('media.lifecycle.read').description), 'media.lifecycle.read says media.object.read guards its routes today');
- ok(/community\.post\.create/.test(capabilities.get('community.vote.set').description) && /community\.comment\.write/.test(capabilities.get('community.vote.set').description), 'community.vote.set says which grants cover its routes today');
+ ok(/space\.post\.write/.test(capabilities.get('community.vote.set').description) && /community\.comment\.write/.test(capabilities.get('community.vote.set').description), 'community.vote.set says which grants cover its routes today');
ok(capabilities.get('live.owner.resolve').implementedBy.length === 0 && capabilities.get('live.owner.resolve').outputSchema === 'lineage.resolution@1', 'live.owner.resolve has no public route yet and answers lineage.resolution@1');
ok(capabilities.get('live.owner.resolve').implementedBy.length === 0 && capabilities.get('live.owner.resolve').outputSchema === 'lineage.resolution@1', 'live.owner.resolve has no public route yet and answers lineage.resolution@1');
ok(JSON.stringify(capabilities.get('search.query.run').implementedBy) === JSON.stringify(capabilities.get('search.query.delegate').implementedBy), 'search.query.run and search.query.delegate are the same routes, asked anonymously or for a person');
ok(JSON.stringify(capabilities.get('search.query.run').implementedBy) === JSON.stringify(capabilities.get('search.query.delegate').implementedBy), 'search.query.run and search.query.delegate are the same routes, asked anonymously or for a person');
const pub = services.get('publishing');
const pub = services.get('publishing');