openvibers/openvibe.contracts Commits
Contracts 0.110.0: OpenVibe.Space and AI's public home launch; Community's forum capabilities retire (#57)
Changes
@@@ -4,6 +4,31 @@ 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.110.0 — 2026-10-07++++**OpenVibe.Space and OpenVibe.AI's public home launch** (plan T10 step 3, T6). It serves the forum at `https://openvibe.space` from++loopback :4940. Community keeps pastes, comments, Pulse and submissions; its former `/s/*` forum paths++redirect permanently to Space.++- `manifests/services/space.json`: `status` alpha, `exposure` live with `publicSite: service`,++ health and readiness paths, lifecycle, and the events Space produces and consumes.++- `space.forum.read`, `space.forum.manage`, `space.thread.read`, and `space.post.write` are active with++ Space's implemented routes. `space.post.write` still guards thread creation; `space.thread.write`++ remains planned as a separate grant. `space.pulse.read` remains planned because Space serves no Pulse.++- `space.thread.created`, `space.post.created`, and `space.moderation.action` have payload contracts and++ valid and invalid fixtures. Space's source is `space`; `space.moderation.action` is++ `common.moderation-action@1` (ADR-022) and OpenVibe.Network consumes it into the moderation audit log.++- Space's problem codes, including `403 space.blocked`, are documented in++ `docs/space-problem-codes.md`; the problem fixture demonstrates the stable URI and code.++- The four former Community forum capabilities are retired and removed from its service list. The seven++ Community forum schemas remain loadable but retired, with Space replacements and a compatibility++ window through 1.0.0. Historical `community.thread.created` and `community.post.created` payloads++ also remain loadable as retired types; Community no longer claims to produce them. Community's++ planned vote grant now describes its surviving comment route only.++- `manifests/services/ai.json`: `exposure` live with `publicSite: service`; `publicOrigin` is++ `https://ai.openvibe.services` (the home, `/stats` and the developer-app API, AI #23), and++ `ai.openvibe.network`, still listed in `domains`, answers 301 to it.++- Generated bundles, types, and OpenAPI documents are refreshed for 0.110.0.++## 0.109.0 — 2026-10-07**OpenVibe.Run runs, OpenVibe.Services registers, and Bot learns buttons and points.** Two schemas gained optional@@@ -21,6 +21,13 @@"inputSchema","outputSchema"]++ },++ {++ "id": "space.forum.manage",++ "missing": [++ "inputSchema",++ "outputSchema"++ ]}]}@@@ -5,50 +5,64 @@"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)"++ "removeAfter": "1.0.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); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"},{"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)"++ "removeAfter": "1.0.0",++ "reason": "the forum data moves to OpenVibe.Space (plan T10, D1/D3); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"},{"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)"++ "removeAfter": "1.0.0",++ "reason": "the forum data moves to OpenVibe.Space (plan T10, D1/D3); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"},{"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)"++ "removeAfter": "1.0.0",++ "reason": "community.space.read is renamed space.forum.read (plan T10, D3); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"},{"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)"++ "removeAfter": "1.0.0",++ "reason": "community.thread.read is renamed space.thread.read (plan T10, D3); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"},{"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)"++ "removeAfter": "1.0.0",++ "reason": "community.post.create is renamed space.post.write (plan T10, D3); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"},{"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)"++ "removeAfter": "1.0.0",++ "reason": "community.post.create is renamed space.post.write (plan T10, D3); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"++ },++ {++ "id": "community.thread.created",++ "replacement": "space.thread.created@1",++ "since": "0.110.0",++ "removeAfter": "1.0.0",++ "reason": "Community forum producer retired after the move to OpenVibe.Space; historical payload remains loadable"++ },++ {++ "id": "community.post.created",++ "replacement": "space.post.created@1",++ "since": "0.110.0",++ "removeAfter": "1.0.0",++ "reason": "Community forum producer retired after the move to OpenVibe.Space; historical payload remains loadable"}]}@@@ -4590,7 +4590,7 @@"schema": "events/payloads/community.thread.created.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "active"++ "status": "retired"},{"id": "community.post.created",@@@ -4599,7 +4599,7 @@"schema": "events/payloads/community.post.created.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "active"++ "status": "retired"},{"id": "community.comment.created",@@@ -5007,7 +5007,7 @@"schema": "community/post-write-request.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "deprecated"++ "status": "retired"},{"id": "community.pulse-write-result",@@@ -5025,7 +5025,7 @@"schema": "community/forum-post.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "deprecated"++ "status": "retired"},{"id": "community.forum-thread",@@@ -5034,7 +5034,7 @@"schema": "community/forum-thread.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "deprecated"++ "status": "retired"},{"id": "community.paste-create-result",@@@ -5052,7 +5052,7 @@"schema": "community/post-write-result.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "deprecated"++ "status": "retired"},{"id": "community.pulse-write-request",@@@ -5106,7 +5106,7 @@"schema": "community/forum-space.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "deprecated"++ "status": "retired"},{"id": "community.pulse-item",@@@ -5133,7 +5133,7 @@"schema": "community/space-read-result.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "deprecated"++ "status": "retired"},{"id": "community.thread-read-result",@@@ -5142,7 +5142,7 @@"schema": "community/thread-read-result.v1.json","visibility": "first-party","compatibility": "backward",-- "status": "deprecated"++ "status": "retired"},{"id": "space.forum-post",@@@ -6229,6 +6229,34 @@"compatibility": "backward","status": "active","adr": "ADR-034"++ },++ {++ "id": "space.thread.created",++ "version": "1.0.0",++ "owner": "space",++ "schema": "events/payloads/space.thread.created.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.post.created",++ "version": "1.0.0",++ "owner": "space",++ "schema": "events/payloads/space.post.created.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.moderation.action",++ "version": "1.0.0",++ "owner": "space",++ "schema": "events/payloads/space.moderation.action.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active",++ "adr": "ADR-022"}]}nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.moderation.action.v1.json",++ "title": "SpaceModerationActionPayload",++ "description": "space.moderation.action v1 (ADR-022; OpenVibe.Space server/events.js moderationAction). A staff or space moderator action on someone else's forum content, or a space moderator assignment: deleting, locking or unlocking a thread, deleting a post, adding or removing a space moderator. Not a person acting on their own content. Written to Space's outbox in the transaction that performs the action; OpenVibe.Network keeps it in the moderation audit log. The payload is common.moderation-action@1. Envelope: subject { type: moderation_action, id: <target type>:<target id> }, visibility internal, actor the moderator. Never carries the content itself.",++ "$ref": "../../common/moderation-action.v1.json"++}nnew file mode 100644@@@ -0,0 +1,55 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.post.created.v1.json",++ "title": "SpacePostCreatedPayload",++ "description": "space.post.created v1 (OpenVibe.Space server/events.js, queued in the transaction of the change). A reply was posted in a forum thread. Never the body. Envelope: subject { type: post, id }, visibility public when the item is public else internal, priority low, actor the person or service:space.",++ "type": "object",++ "required": [++ "post_id",++ "thread_id",++ "space",++ "author",++ "visibility",++ "url"++ ],++ "additionalProperties": false,++ "properties": {++ "post_id": {++ "type": "integer",++ "minimum": 1++ },++ "thread_id": {++ "type": "integer",++ "minimum": 1++ },++ "space": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9-]{0,62}$"++ },++ "author": {++ "oneOf": [++ {++ "$ref": "../../identity/subject-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]++ },++ "visibility": {++ "enum": [++ "public",++ "members",++ "hidden"++ ]++ },++ "url": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500,++ "description": "The public page, or null when the item is not public."++ }++ }++}nnew file mode 100644@@@ -0,0 +1,50 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.thread.created.v1.json",++ "title": "SpaceThreadCreatedPayload",++ "description": "space.thread.created v1 (OpenVibe.Space server/events.js, queued in the transaction of the change). A forum thread was started in a space. Envelope: subject { type: thread, id }, visibility public when the item is public else internal, priority low, actor the person or service:space.",++ "type": "object",++ "required": [++ "thread_id",++ "space",++ "author",++ "visibility",++ "url"++ ],++ "additionalProperties": false,++ "properties": {++ "thread_id": {++ "type": "integer",++ "minimum": 1++ },++ "space": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9-]{0,62}$"++ },++ "author": {++ "oneOf": [++ {++ "$ref": "../../identity/subject-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]++ },++ "visibility": {++ "enum": [++ "public",++ "members",++ "hidden"++ ]++ },++ "url": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500,++ "description": "The public page, or null when the item is not public."++ }++ }++}@@@ -2,7 +2,7 @@"$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, …).",++ "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, 403 space.blocked, …); see docs/space-problem-codes.md.","anyOf": [{"type": "object",nnew file mode 100644@@@ -0,0 +1,25 @@++# OpenVibe.Space problem codes++++Space returns `errors.problem@1` (`application/problem+json`). Its `code` is stable and its default `type` is `https://openvibe.network/problems/<code>`. Space's `server/forum/service.js`, `server/identity/blocks.js`, `server/forum/routes.js`, and `server/events-consumer.js` issue these `space.*` codes:++++| Status | Code | Meaning |++| --- | --- | --- |++| 403 | `space.blocked` | The author or moderator target has a blocking relationship with the caller. This also refuses adding a space moderator when either person blocked the other. |++| 404 | `space.not_found` | The space or requested parent space does not exist, or a staff space is hidden from the caller. |++| 403 | `space.staff_only` | Only discussion staff can post in this space. |++| 403 | `space.staff_threads` | Only staff can start a Roadmap thread. |++| 400 | `space.invalid_name` | A space needs a valid name. |++| 400 | `space.invalid_style` | The style must be `feed` or `forum`. |++| 400 | `space.invalid_kind` | The kind must be `discussion`, `request`, or `roadmap`. |++| 400 | `space.invalid_parent` | The parent creates a cycle or nests boards too deeply. |++| 400 | `space.invalid_slug` | The requested slug is invalid or reserved. |++| 409 | `space.slug_taken` | The requested slug already belongs to a space. |++| 403 | `space.reactions_off` | Ratings are off in this space. |++| 403 | `space.votes_off` | Votes are off in this space. |++| 403 | `space.internal_only` | The Events delivery route only accepts local requests. |++| 503 | `space.events_disabled` | The Events delivery secret is not configured. |++| 401 | `space.bad_signature` | The Events delivery signature is invalid or outside its replay window. |++| 400 | `space.bad_delivery` | The Events delivery body is malformed. |++| 500 | `space.event_failed` | Processing an Events delivery failed; Events may retry it. |++++`community.blocked` remains a Community code for its surviving comment and paste routes. Space uses its own `space.blocked` namespace for forum operations.nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "type": "https://openvibe.network/problems/space.blocked",++ "title": "Forbidden",++ "status": 403,++ "code": "space.blocked",++ "detail": "One of you blocked the other"++}nnew file mode 100644@@@ -0,0 +1,9 @@++{++ "action": "post.deleted",++ "target": {++ "type": "post",++ "id": "31"++ },++ "actor_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "content": "private body"++}nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "action": "space.moderator_added",++ "target": { "type": "space", "id": "general", "owner_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },++ "actor_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "reason": null,++ "details": {}++}nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "action": "space.moderator_removed",++ "target": { "type": "space", "id": "general", "owner_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },++ "actor_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "reason": null,++ "details": {}++}nnew file mode 100644@@@ -0,0 +1,11 @@++{++ "action": "thread.locked",++ "target": {++ "type": "thread",++ "id": "31",++ "owner_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR"++ },++ "actor_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "reason": null,++ "details": {}++}nnew file mode 100644@@@ -0,0 +1,12 @@++{++ "post_id": 31,++ "thread_id": 7,++ "space": "general",++ "author": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "visibility": "public",++ "url": "https://openvibe.space/s/general/7#p31",++ "body": "the content never travels"++}nnew file mode 100644@@@ -0,0 +1,11 @@++{++ "post_id": 31,++ "thread_id": 7,++ "space": "general",++ "author": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "visibility": "public",++ "url": "https://openvibe.space/s/general/7#p31"++}nnew file mode 100644@@@ -0,0 +1,11 @@++{++ "thread_id": 7,++ "space": "general",++ "author": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "visibility": "public",++ "url": "https://openvibe.space/s/general/7",++ "body": "the content never travels"++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "thread_id": 7,++ "space": "general",++ "author": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "visibility": "public",++ "url": "https://openvibe.space/s/general/7"++}@@@ -49830,7 +49830,7 @@"community.thread.created": {"version": "1.0.0","owner": "community",-- "status": "active",++ "status": "retired","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/events/payloads/community.thread.created.v1.json",@@@ -49885,7 +49885,7 @@"community.post.created": {"version": "1.0.0","owner": "community",-- "status": "active",++ "status": "retired","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/events/payloads/community.post.created.v1.json",@@@ -56611,7 +56611,7 @@"community.post-write-request": {"version": "1.0.0","owner": "community",-- "status": "deprecated",++ "status": "retired","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/post-write-request.v1.json",@@@ -56778,7 +56778,7 @@"community.forum-post": {"version": "1.0.0","owner": "community",-- "status": "deprecated",++ "status": "retired","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/forum-post.v1.json",@@@ -56973,7 +56973,7 @@"community.forum-thread": {"version": "1.0.0","owner": "community",-- "status": "deprecated",++ "status": "retired","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/forum-thread.v1.json",@@@ -57258,7 +57258,7 @@"community.post-write-result": {"version": "1.0.0","owner": "community",-- "status": "deprecated",++ "status": "retired","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/post-write-result.v1.json",@@@ -57847,7 +57847,7 @@"community.forum-space": {"version": "1.0.0","owner": "community",-- "status": "deprecated",++ "status": "retired","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/forum-space.v1.json",@@@ -58149,7 +58149,7 @@"community.space-read-result": {"version": "1.0.0","owner": "community",-- "status": "deprecated",++ "status": "retired","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/space-read-result.v1.json",@@@ -58224,7 +58224,7 @@"community.thread-read-result": {"version": "1.0.0","owner": "community",-- "status": "deprecated",++ "status": "retired","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/community/thread-read-result.v1.json",@@@ -59034,7 +59034,7 @@"$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, …).",++ "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, 403 space.blocked, …); see docs/space-problem-codes.md.","anyOf": [{"type": "object",@@@ -72453,6 +72453,133 @@}}}++ },++ "space.thread.created": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.thread.created.v1.json",++ "title": "SpaceThreadCreatedPayload",++ "description": "space.thread.created v1 (OpenVibe.Space server/events.js, queued in the transaction of the change). A forum thread was started in a space. Envelope: subject { type: thread, id }, visibility public when the item is public else internal, priority low, actor the person or service:space.",++ "type": "object",++ "required": [++ "thread_id",++ "space",++ "author",++ "visibility",++ "url"++ ],++ "additionalProperties": false,++ "properties": {++ "thread_id": {++ "type": "integer",++ "minimum": 1++ },++ "space": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9-]{0,62}$"++ },++ "author": {++ "oneOf": [++ {++ "$ref": "../../identity/subject-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]++ },++ "visibility": {++ "enum": [++ "public",++ "members",++ "hidden"++ ]++ },++ "url": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500,++ "description": "The public page, or null when the item is not public."++ }++ }++ }++ },++ "space.post.created": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.post.created.v1.json",++ "title": "SpacePostCreatedPayload",++ "description": "space.post.created v1 (OpenVibe.Space server/events.js, queued in the transaction of the change). A reply was posted in a forum thread. Never the body. Envelope: subject { type: post, id }, visibility public when the item is public else internal, priority low, actor the person or service:space.",++ "type": "object",++ "required": [++ "post_id",++ "thread_id",++ "space",++ "author",++ "visibility",++ "url"++ ],++ "additionalProperties": false,++ "properties": {++ "post_id": {++ "type": "integer",++ "minimum": 1++ },++ "thread_id": {++ "type": "integer",++ "minimum": 1++ },++ "space": {++ "type": "string",++ "pattern": "^[a-z0-9][a-z0-9-]{0,62}$"++ },++ "author": {++ "oneOf": [++ {++ "$ref": "../../identity/subject-ref.v1.json"++ },++ {++ "type": "null"++ }++ ]++ },++ "visibility": {++ "enum": [++ "public",++ "members",++ "hidden"++ ]++ },++ "url": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500,++ "description": "The public page, or null when the item is not public."++ }++ }++ }++ },++ "space.moderation.action": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.moderation.action.v1.json",++ "title": "SpaceModerationActionPayload",++ "description": "space.moderation.action v1 (ADR-022; OpenVibe.Space server/events.js moderationAction). A staff or space moderator action on someone else's forum content, or a space moderator assignment: deleting, locking or unlocking a thread, deleting a post, adding or removing a space moderator. Not a person acting on their own content. Written to Space's outbox in the transaction that performs the action; OpenVibe.Network keeps it in the moderation audit log. The payload is common.moderation-action@1. Envelope: subject { type: moderation_action, id: <target type>:<target id> }, visibility internal, actor the moderator. Never carries the content itself.",++ "$ref": "../../common/moderation-action.v1.json"++ }}}}@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.109.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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"@@@ -12,7 +12,7 @@},"servers": [{-- "url": "https://ai.openvibe.network"++ "url": "https://ai.openvibe.services"}],"paths": {@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Billing API",-- "version": "0.109.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bot API",-- "version": "0.109.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.Coupons API",-- "version": "0.109.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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"@@@ -4,7 +4,7 @@{"service": "ai","name": "OpenVibe.AI",-- "origin": "https://ai.openvibe.network",++ "origin": "https://ai.openvibe.services","operations": 43,"capabilities": 8},@@@ -159,8 +159,8 @@"service": "space","name": "OpenVibe.Space","origin": "https://openvibe.space",-- "operations": 15,-- "capabilities": 3++ "operations": 30,++ "capabilities": 4},{"service": "tips",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.109.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Run API",-- "version": "0.109.0",-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Services API",-- "version": "0.109.0",-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Space API",-- "version": "0.109.0",-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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"@@@ -139,9 +139,592 @@}}},++ "/api/v1/posts/{id}/reactions": {++ "post": {++ "operationId": "post_api_v1_posts_id_reactions",++ "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/space-groups": {++ "get": {++ "operationId": "get_api_v1_space_groups",++ "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/space-groups/{group}": {++ "put": {++ "operationId": "put_api_v1_space_groups_group",++ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",++ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",++ "tags": [++ "space.forum"++ ],++ "x-openvibe-capabilities": [++ "space.forum.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "space-moderate",++ "security": [++ {++ "openvibeToken": [++ "space.forum.manage"++ ]++ }++ ],++ "parameters": [++ {++ "name": "group",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/api/v1/spaces": {"get": {-- "operationId": "get_api_v1_spaces",++ "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"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_api_v1_spaces",++ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",++ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",++ "tags": [++ "space.forum"++ ],++ "x-openvibe-capabilities": [++ "space.forum.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "space-moderate",++ "security": [++ {++ "openvibeToken": [++ "space.forum.manage"++ ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/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}/attachments": {++ "post": {++ "operationId": "post_api_v1_spaces_space_attachments",++ "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}/categories": {++ "get": {++ "operationId": "get_api_v1_spaces_space_categories",++ "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}/categories/{category}": {++ "delete": {++ "operationId": "delete_api_v1_spaces_space_categories_category",++ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",++ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",++ "tags": [++ "space.forum"++ ],++ "x-openvibe-capabilities": [++ "space.forum.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "space-moderate",++ "security": [++ {++ "openvibeToken": [++ "space.forum.manage"++ ]++ }++ ],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "category",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_api_v1_spaces_space_categories_category",++ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",++ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",++ "tags": [++ "space.forum"++ ],++ "x-openvibe-capabilities": [++ "space.forum.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "space-moderate",++ "security": [++ {++ "openvibeToken": [++ "space.forum.manage"++ ]++ }++ ],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "category",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/spaces/{space}/members-only": {++ "put": {++ "operationId": "put_api_v1_spaces_space_members_only",++ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",++ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",++ "tags": [++ "space.forum"++ ],++ "x-openvibe-capabilities": [++ "space.forum.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "space-moderate",++ "security": [++ {++ "openvibeToken": [++ "space.forum.manage"++ ]++ }++ ],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/spaces/{space}/moderators": {++ "get": {++ "operationId": "get_api_v1_spaces_space_moderators","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": [@@@ -161,6 +744,16 @@]}],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],"responses": {"2XX": {"description": "Success",@@@ -185,25 +778,25 @@}}},-- "/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.",++ "/api/v1/spaces/{space}/moderators/{subject}": {++ "delete": {++ "operationId": "delete_api_v1_spaces_space_moderators_subject",++ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",++ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.","tags": ["space.forum"],"x-openvibe-capabilities": [-- "space.forum.read"++ "space.forum.manage"],"x-openvibe-visibility": [-- "public"++ "internal"],-- "x-openvibe-quota-class": "space-read",++ "x-openvibe-quota-class": "space-moderate","security": [{"openvibeToken": [-- "space.forum.read"++ "space.forum.manage"]}],@@@ -215,18 +808,123 @@"schema": {"type": "string"}++ },++ {++ "name": "subject",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }}],"responses": {"2XX": {-- "description": "Success",++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)","content": {-- "application/json": {++ "application/problem+json": {"schema": {-- "$ref": "#/components/schemas/space.forum-read-result.v1"++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_api_v1_spaces_space_moderators_subject",++ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",++ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",++ "tags": [++ "space.forum"++ ],++ "x-openvibe-capabilities": [++ "space.forum.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "space-moderate",++ "security": [++ {++ "openvibeToken": [++ "space.forum.manage"++ ]++ }++ ],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "subject",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"}}}++ }++ }++ }++ },++ "/api/v1/spaces/{space}/settings": {++ "put": {++ "operationId": "put_api_v1_spaces_space_settings",++ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",++ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",++ "tags": [++ "space.forum"++ ],++ "x-openvibe-capabilities": [++ "space.forum.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "space-moderate",++ "security": [++ {++ "openvibeToken": [++ "space.forum.manage"++ ]++ }++ ],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"},"default": {"description": "An error (RFC 9457 problem details)",@@@ -245,7 +943,7 @@"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.",++ "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.","tags": ["space.thread"],@@@ -430,7 +1128,7 @@"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.",++ "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.","tags": ["space.thread"],@@@ -490,6 +1188,80 @@}}},++ "/api/v1/spaces/{space}/threads/{slug}/crosspost": {++ "post": {++ "operationId": "post_api_v1_spaces_space_threads_slug_crosspost",++ "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}/posts": {"post": {"operationId": "post_api_v1_spaces_space_threads_slug_posts",@@@ -564,6 +1336,63 @@}}},++ "/api/v1/spaces/{space}/threads/{slug}/state": {++ "put": {++ "operationId": "put_api_v1_spaces_space_threads_slug_state",++ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",++ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",++ "tags": [++ "space.forum"++ ],++ "x-openvibe-capabilities": [++ "space.forum.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "space-moderate",++ "security": [++ {++ "openvibeToken": [++ "space.forum.manage"++ ]++ }++ ],++ "parameters": [++ {++ "name": "space",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "slug",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/api/v1/spaces/{space}/threads/{slug}/votes": {"post": {"operationId": "post_api_v1_spaces_space_threads_slug_votes",@@@ -688,7 +1517,7 @@"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.",++ "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.","tags": ["space.thread"],@@@ -744,7 +1573,7 @@"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.",++ "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.","tags": ["space.thread"],@@@ -800,7 +1629,7 @@"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.",++ "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.","tags": ["space.thread"],@@@ -864,7 +1693,7 @@"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.",++ "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.","tags": ["space.thread"],@@@ -1621,7 +2450,7 @@},"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, …).",++ "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, 403 space.blocked, …); see docs/space-problem-codes.md.","anyOf": [{"type": "object",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tips API",-- "version": "0.109.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Watch API",-- "version": "0.109.0",-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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"@@@ -53417,7 +53417,7 @@ export interface SpaceForumThread {/** [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, …).++ * 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, 403 space.blocked, …); see docs/space-problem-codes.md.*/export type SpacePostWriteRequest =| {@@@ -58669,3 +58669,65 @@ export interface NetworkNodeRevokedPayload {*/at: string;}++++/** [email protected] (owner: space) */++/**++ * space.thread.created v1 (OpenVibe.Space server/events.js, queued in the transaction of the change). A forum thread was started in a space. Envelope: subject { type: thread, id }, visibility public when the item is public else internal, priority low, actor the person or service:space.++ */++export interface SpaceThreadCreatedPayload {++ thread_id: number;++ space: string;++ author: SubjectRef | null;++ visibility: "public" | "members" | "hidden";++ /**++ * The public page, or null when the item is not public.++ */++ url: string | null;++}++++/** [email protected] (owner: space) */++/**++ * space.post.created v1 (OpenVibe.Space server/events.js, queued in the transaction of the change). A reply was posted in a forum thread. Never the body. Envelope: subject { type: post, id }, visibility public when the item is public else internal, priority low, actor the person or service:space.++ */++export interface SpacePostCreatedPayload {++ post_id: number;++ thread_id: number;++ space: string;++ author: SubjectRef | null;++ visibility: "public" | "members" | "hidden";++ /**++ * The public page, or null when the item is not public.++ */++ url: string | null;++}++++/** [email protected] (owner: space) */++/**++ * space.moderation.action v1 (ADR-022; OpenVibe.Space server/events.js moderationAction). A staff or space moderator action on someone else's forum content, or a space moderator assignment: deleting, locking or unlocking a thread, deleting a post, adding or removing a space moderator. Not a person acting on their own content. Written to Space's outbox in the transaction that performs the action; OpenVibe.Network keeps it in the moderation audit log. The payload is common.moderation-action@1. Envelope: subject { type: moderation_action, id: <target type>:<target id> }, visibility internal, actor the moderator. Never carries the content itself.++ */++export interface SpaceModerationActionPayload {++ /**++ * A short verb id, as the service names it: <thing>.<what happened> (post.hidden, user.banned, release.revoked, guard.blocked).++ */++ action: string;++ target: {++ /**++ * What was acted on, in the service's own words (post, page, release, user, player, …).++ */++ type: string;++ id: string;++ /**++ * Whose content or account it was, when known.++ */++ owner_subject?: string | null;++ };++ /**++ * The staff member or moderator (null only for a service acting without a person).++ */++ actor_subject: string | null;++ reason?: string | null;++ /**++ * Per action: the fields changed, the previous and new state, a bulk action's count, … never the content.++ */++ details?: {};++}@@@ -2,10 +2,10 @@"id": "community.post.create","version": "1.1","owner": "community",-- "status": "deprecated",++ "status": "retired","visibility": "public","sensitive": true,-- "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.",++ "description": "RETIRED at 0.110.0: OpenVibe.Community no longer serves this forum capability. Use space.post.write on OpenVibe.Space. Community /s/* paths redirect permanently to openvibe.space.","outputSchema": "community.post-write-result@1","permissions": ["community:write"@@@ -14,16 +14,7 @@"space_member"],"quotaClass": "community-write",-- "events": [-- "community.thread.created",-- "community.post.created"-- ],-- "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"-- ],++ "events": [],++ "implementedBy": [],"inputSchema": "community.post-write-request@1"}@@@ -2,9 +2,9 @@"id": "community.space.manage","version": "1.0","owner": "community",-- "status": "deprecated",++ "status": "retired","visibility": "internal",-- "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.",++ "description": "RETIRED at 0.110.0: OpenVibe.Community no longer serves this forum capability. Use space.forum.manage on OpenVibe.Space. Community /s/* paths redirect permanently to openvibe.space.","permissions": ["community:moderate"],@@@ -13,7 +13,5 @@],"quotaClass": "community-moderate","events": [],-- "implementedBy": [-- "PUT /api/v1/spaces/:space/members-only"-- ]++ "implementedBy": []}@@@ -2,9 +2,9 @@"id": "community.space.read","version": "1.0","owner": "community",-- "status": "deprecated",++ "status": "retired","visibility": "public",-- "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.",++ "description": "RETIRED at 0.110.0: OpenVibe.Community no longer serves this forum capability. Use space.forum.read on OpenVibe.Space. Community /s/* paths redirect permanently to openvibe.space.","outputSchema": "community.space-read-result@1","permissions": ["community:read"@@@ -14,10 +14,6 @@],"quotaClass": "community-read","events": [],-- "implementedBy": [-- "GET /api/v1/spaces",-- "GET /api/v1/spaces/:space",-- "GET /s"-- ],++ "implementedBy": [],"inputSchema": "common.no-body@1"}@@@ -2,9 +2,9 @@"id": "community.thread.read","version": "1.0","owner": "community",-- "status": "deprecated",++ "status": "retired","visibility": "public",-- "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.",++ "description": "RETIRED at 0.110.0: OpenVibe.Community no longer serves this forum capability. Use space.thread.read on OpenVibe.Space. Community /s/* paths redirect permanently to openvibe.space.","outputSchema": "community.thread-read-result@1","permissions": ["community:read"@@@ -14,13 +14,6 @@],"quotaClass": "community-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"-- ],++ "implementedBy": [],"inputSchema": "common.no-body@1"}@@@ -4,19 +4,17 @@"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 space.post.write (threads) and community.comment.write (comments), which keep covering them.",++ "description": "PLANNED as its own grant for Community comments: up-vote, down-vote or clear a vote (value 1, -1 or 0; 0 removes it). Community.comment.write guards the comment vote route today. Forum thread votes moved to OpenVibe.Space and are guarded there by space.post.write.","inputSchema": "common.entity-ref@1","permissions": ["community:vote"],"resourceConstraints": [-- "space_member"++ "none"],"quotaClass": "community-vote","events": [],"implementedBy": [-- "POST /api/v1/spaces/:space/threads/:slug/votes",-- "POST /api/v1/comments/:commentId/votes",-- "POST /s/:space/t/:slug/vote"++ "POST /api/v1/comments/:commentId/votes"]}@@@ -2,9 +2,9 @@"id": "space.forum.manage","version": "1.0","owner": "space",-- "status": "planned",++ "status": "active","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.",++ "description": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.","permissions": ["space:moderate"],@@@ -12,8 +12,16 @@"none"],"quotaClass": "space-moderate",-- "events": [],++ "events": [++ "space.moderation.action"++ ],"implementedBy": [-- "PUT /api/v1/spaces/:space/members-only"++ "POST /api/v1/spaces",++ "PUT /api/v1/spaces/:space/settings",++ "PUT /api/v1/spaces/:space/members-only",++ "PUT|DELETE /api/v1/spaces/:space/categories/:category",++ "PUT|DELETE /api/v1/spaces/:space/moderators/:subject",++ "PUT /api/v1/spaces/:space/threads/:slug/state",++ "PUT /api/v1/space-groups/:group"]}@@@ -17,7 +17,10 @@"implementedBy": ["GET /api/v1/spaces","GET /api/v1/spaces/:space",-- "GET /s"++ "GET /s",++ "GET /api/v1/spaces/:space/categories",++ "GET /api/v1/spaces/:space/moderators",++ "GET /api/v1/space-groups"],"inputSchema": "common.no-body@1"}@@@ -14,13 +14,19 @@"space_member"],"quotaClass": "space-write",-- "events": [],++ "events": [++ "space.thread.created",++ "space.post.created"++ ],"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"++ "DELETE /api/v1/spaces/:space/threads/:slug",++ "POST /api/v1/spaces/:space/attachments",++ "POST /api/v1/spaces/:space/threads/:slug/crosspost",++ "POST /api/v1/posts/:id/reactions"],"inputSchema": "space.post-write-request@1"}@@@ -4,7 +4,7 @@"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.",++ "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.","outputSchema": "space.thread-read-result@1","permissions": ["space:read"@@@ -8,16 +8,17 @@"status": "alpha","repository": "OpenVibers/OpenVibe.AI","domains": [++ "ai.openvibe.services","ai.openvibe.network"],-- "publicOrigin": "https://ai.openvibe.network",++ "publicOrigin": "https://ai.openvibe.services","health": "/api/health","ready": "/api/ready","internalOrigin": "http://127.0.0.1:4700","exposure": {-- "state": "internal",-- "publicSite": null,-- "note": "called by other services at 127.0.0.1:4700; ai.openvibe.network serves the OpenVibe.Sites placeholder page"++ "state": "live",++ "publicSite": "service",++ "note": "the public home, /stats and the developer-app API (ai.app.run) at ai.openvibe.services since 2026-10-07 (AI #23); ai.openvibe.network answers 301 to it; services on the host call 127.0.0.1:4700"},"capabilities": ["ai.app.run",@@@ -76,14 +77,14 @@],"window": "automatic only while ovhost waits for readiness after the restart (30 s in host.example.json); manual afterwards, as long as no blocker applies","blockers": {-- "none": "the schema code only adds (CREATE … IF NOT EXISTS, ADD COLUMN; no DROP, RENAME or table rebuild), which an older release ignores"++ "none": "the schema code only adds (CREATE \u2026 IF NOT EXISTS, ADD COLUMN; no DROP, RENAME or table rebuild), which an older release ignores"}},"contracts": {"range": ">=0.6.0 <1.0.0"},"leases": {-- "none": "no claims: one process owns ai.db (better-sqlite3, single writer); a run is taken with a compare-and-set on its status (queued → running)"++ "none": "no claims: one process owns ai.db (better-sqlite3, single writer); a run is taken with a compare-and-set on its status (queued \u2192 running)"}},"notes": "Providers, models, routing profiles, versioned prompt templates and workflows, runs, request logs, citations, scoped cache, quotas and audit. Workflow output is a draft/evidence package attributed to workflow + model + run. ai.run.queued|succeeded|failed|cached have payload contracts (v0.30.0, status planned): AI publishes them once its Events client is wired in; it publishes none today. ai.run.create takes ai.run-request@1 and returns ai.run@1."@@@ -16,13 +16,9 @@"community.paste.create","community.paste.write","community.paste.moderate",-- "community.post.create","community.comment.write","community.comment.moderate","community.pulse.write",-- "community.space.read",-- "community.space.manage",-- "community.thread.read","community.vote.set","community.pulse.read"],@@@ -33,9 +29,7 @@"community.moderation.action","community.paste.created","community.paste.deleted",-- "community.paste.updated",-- "community.post.created",-- "community.thread.created"++ "community.paste.updated"],"eventsConsumed": ["network.account.deleted",@@@ -71,7 +65,7 @@"drains": ["Pulse subscription retries, the community.profile scan and the search-document scans stopped","stops taking connections; idle keep-alive connections closed (kept 65 s otherwise); requests in flight answered with Connection: close and allowed to finish (4 s at most, then cut)",-- "the profile scan in progress (it stops between people), the Discord relay's drain and the events outbox's send awaited; unsent rows stay in their tables",++ "the profile scan in progress (it stops between people) and the events outbox's send awaited; unsent rows stay in their tables","community.db closed, exit 0"],"note": "server/graceful.js; a stop that passes 5 s exits 1. Only the in-memory rate-limit sweeps end with the process. The unit sets no TimeoutStopSec (systemd's 90 s default applies)"@@@ -84,7 +78,7 @@},{"kind": "schedule",-- "what": "the Roadmap space re-syncs from docs/roadmap/public.json; the community.profile and search-document scans start again from what changed"++ "what": "the community.profile and search-document scans restart from what changed"},{"kind": "consumer",@@@ -108,5 +102,6 @@"leases": {"none": "no claims: one process owns community.db (better-sqlite3, single writer)"}-- }++ },++ "notes": "Community continues to serve pastes, comments, Pulse and submissions. Its former /s/* forum paths issue 301 redirects to openvibe.space; Space owns forum data and writes."}@@@ -99,6 +99,7 @@"media.moderation.action","news.moderation.action","reviews.moderation.action",++ "space.moderation.action","tips.interaction.moderated","tools.moderation.action","tools.usage.recorded",@@@ -5,16 +5,15 @@},"id": "space","name": "OpenVibe.Space",-- "status": "placeholder",++ "status": "alpha","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"++ "state": "live",++ "publicSite": "service"},"site": {"name": "Space",@@@ -25,19 +24,19 @@"position": 19,"tld": "space","accent": "#818cf8",-- "description": "OpenVibe.Space will be the forums of the OpenVibe network: create a forum for any topic, with the depth of classic boards like XenForo and phpBB, the feel of a modern community, and AI help for topics, research and moderation. The forums on OpenVibe.Community move here when it launches.",++ "description": "OpenVibe.Space is the network forum: browse public and members-only boards, start threads, reply, vote and moderate spaces with one OpenVibe identity.","pillars": [{"title": "Any forum",-- "text": "Anyone can start one, for any topic."++ "text": "Browse boards for discussions, requests and the roadmap."},{"title": "Classic depth",-- "text": "Sub-forums, threads, ranks, moderation tools."++ "text": "Child boards, threads, categories and moderation tools."},{-- "title": "AI assist",-- "text": "Topic ideas, research and summaries, always labelled."++ "title": "Make it yours",++ "text": "Moderators can shape boards, categories and visibility."},{"title": "One account",@@@ -45,50 +44,80 @@}],"keywords": "openvibe space, forums, create a forum, community boards, xenforo alternative, phpbb alternative",-- "launch": "the forum engine moving over from OpenVibe.Community",-- "relationships": {-- "noRepo": true,-- "meanwhile": [-- {-- "label": "OpenVibe.Community",-- "url": "https://openvibe.community",-- "note": "Forums, pastes and people live there today."-- }-- ]-- }++ "launch": "Forums moved from OpenVibe.Community to OpenVibe.Space (plan T10 step 3)."},"capabilities": ["space.forum.read","space.forum.manage","space.thread.read",-- "space.thread.write",-- "space.post.write",-- "space.pulse.read"++ "space.post.write"++ ],++ "eventsProduced": [++ "space.thread.created",++ "space.post.created",++ "space.moderation.action"++ ],++ "eventsConsumed": [++ "network.user.token_valid_after",++ "network.block.changed",++ "network.subject.merged",++ "network.account.export_requested",++ "network.account.deleted",++ "vip.membership.changed"],-- "eventsProduced": [],-- "eventsConsumed": [],"namespacesOwned": ["space.*"],"lifecycle": {"liveness": {-- "none": "planned service: no OpenVibe.Space process runs yet (plan T10 item 3)"++ "endpoint": "/api/health",++ "means": "the Space process answers HTTP; /api/ready checks PostgreSQL and reports degraded dependencies"},"shutdown": {-- "none": "planned service: nothing to stop yet"++ "signal": "SIGTERM",++ "deadlineSeconds": 5,++ "drains": [++ "subscription retries stop; the HTTP server stops accepting requests and gives in-flight requests up to 4 seconds",++ "the Discord relay and Events outbox finish their current sends; unsent rows remain for the next start",++ "the PostgreSQL pool closes and the process exits"++ ],++ "note": "server/index.js uses openvibe-sdk/service gracefulStop with a 4-second drain and 5-second deadline"},"startupRecovery": {-- "none": "planned service: nothing runs to resume"++ "resumes": [++ {++ "kind": "outbox",++ "what": "unsent space.* events in the PostgreSQL outbox relay after restart (at least once)"++ },++ {++ "kind": "consumer",++ "what": "Events subscriptions are recreated and pending network and VIP deliveries are retried idempotently"++ },++ {++ "kind": "schedule",++ "what": "the Roadmap space resyncs from docs/roadmap/public.json and Discord relay workers restart"++ }++ ]},"rollback": {-- "none": "planned service: nothing is deployed to roll back"++ "conditions": [++ "automatic: ovhost deploy restores the prior sha if /api/ready fails in its readiness window",++ "manual: ovhost rollback space --to <sha>"++ ],++ "window": "automatic during deploy readiness; manual afterwards while the previous code is database compatible",++ "blockers": {++ "none": "the initial PostgreSQL migration creates the Space schema without dropping earlier data"++ }},"contracts": {"range": ">=0.82.0 <1.0.0"},"leases": {-- "none": "planned service: no process, no claims"++ "none": "PostgreSQL outbox workers coordinate delivery; no separate service-owned lease is exposed"}},-- "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."++ "notes": "Space owns forum spaces, threads, posts, votes, categories, reactions, moderation and the Discord relay in its PostgreSQL database. OpenVibe.Community retains pastes, comments, Pulse and submissions; legacy /s/* pages redirect permanently to openvibe.space.",++ "health": "/api/health",++ "ready": "/api/ready",++ "internalOrigin": "http://127.0.0.1:4940"}@@@ -1,12 +1,12 @@{"name": "openvibe-contracts",-- "version": "0.109.0",++ "version": "0.110.0","lockfileVersion": 3,"requires": true,"packages": {"": {"name": "openvibe-contracts",-- "version": "0.109.0",++ "version": "0.110.0","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.109.0",++ "version": "0.110.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",@@@ -89,7 +89,8 @@ for (const cap of capabilities.manifests) {const r = contracts.validate('capabilities.capability@1', cap);ok(r.valid, `capability ${cap.id}: ${JSON.stringify(r.errors)}`);ok(serviceIds.has(cap.owner), `${cap.id} owner ${cap.owner} is a registered service`);-- ok(services.get(cap.owner).capabilities.includes(cap.id), `${cap.owner} manifest lists ${cap.id}`);++ ok(['planned', 'retired'].includes(cap.status) || services.get(cap.owner).capabilities.includes(cap.id), `${cap.owner} manifest lists served ${cap.id}`);++ if (cap.status === 'retired') ok(!services.get(cap.owner).capabilities.includes(cap.id), `${cap.owner} no longer offers retired ${cap.id}`);for (const k of ['inputSchema', 'outputSchema']) if (cap[k]) ok(contracts.resolve(cap[k]), `${cap.id} ${k} resolves`);ok(cap.status !== 'active' || cap.implementedBy.length > 0, `active ${cap.id} names the route that implements it`);}@@@ -121,8 +122,9 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscriptconst major = Number(c.version.split('.')[0]);ok(c.schema === `events/payloads/${c.id}.v${major}.json`, `${c.id} lives at events/payloads/<event_type>.v<major>.json`);ok(/^[a-z][a-z0-9_]*(\.[a-z0-9_]+){2,}$/.test(c.id), `${c.id} is an event type`);-- ok(services.get(c.owner) && services.get(c.owner).eventsProduced.includes(c.id), `${c.owner} manifest lists ${c.id} in eventsProduced`);-- ok(['active', 'planned'].includes(c.status), `${c.id} status ${c.status}`);++ ok(services.get(c.owner) && (c.status === 'retired' || services.get(c.owner).eventsProduced.includes(c.id)), `${c.owner} manifest lists current ${c.id} in eventsProduced`);++ ok(['active', 'planned', 'retired'].includes(c.status), `${c.id} status ${c.status}`);++ if (c.status === 'retired') ok(!services.get(c.owner).eventsProduced.includes(c.id), `${c.owner} no longer produces retired ${c.id}`);const s = contracts.schema(c.id);ok((s.type === 'object' || s.$ref || s.allOf) && s.title.endsWith('Payload'), `${c.id} is an object payload schema`);if (s.properties && s.properties.redacts) ok(s.properties.redacts.$ref === '../redaction-directive.v1.json', `${c.id} redacts uses events.redaction-directive@1`);@@@ -466,7 +468,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.vote.set': 'planned', 'community.pulse.read': 'active', 'space.forum.read': 'active', 'space.forum.manage': 'planned', 'space.thread.read': 'active',++ 'community.vote.set': 'planned', 'community.pulse.read': 'active', 'space.forum.read': 'active', 'space.thread.read': 'active','search.query.run': 'active',};for (const [id, status] of Object.entries(gaps)) {@@@ -475,9 +477,13 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscriptif (status === 'planned') ok(c.description.startsWith('PLANNED'), `${id}: a planned capability says so first`);if (status === 'active') ok(c.visibility === 'public' && c.implementedBy.every(r => r.startsWith('GET ')), `${id}: an active §30.2 read is a public GET`);}++ ok(capabilities.get('space.forum.manage').status === 'active' && capabilities.get('space.forum.manage').implementedBy.includes('POST /api/v1/spaces'), 'space.forum.manage is served by Space');++ ok(capabilities.get('space.thread.write').status === 'planned' && !services.get('space').capabilities.includes('space.thread.write'), 'space.thread.write is not a separate grant for the existing create route');++ ok(capabilities.get('space.pulse.read').status === 'planned' && !services.get('space').capabilities.includes('space.pulse.read'), 'Space does not serve Pulse');++ for (const id of ['community.space.read', 'community.space.manage', 'community.thread.read', 'community.post.create']) ok(capabilities.get(id).status === 'retired' && !services.get('community').capabilities.includes(id), `${id} retired from Community`);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(/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(/space\.post\.write/i.test(capabilities.get('community.vote.set').description) && /community\.comment\.write/i.test(capabilities.get('community.vote.set').description) && capabilities.get('community.vote.set').implementedBy.length === 1, 'community.vote.set retains only the comment route');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');@@@ -799,7 +805,7 @@ await assert.rejects(failing.getToken(), /401: invalid_client/);// without an older shape are common.moderation-action@1, and Network consumes every one of them.{const moderation = contracts.catalog.filter(c => /\.moderation\.action$/.test(c.id));-- for (const svc of ['media', 'tools', 'games', 'wiki', 'blog', 'news', 'reviews', 'deals', 'coupons', 'trade', 'codes']) {++ for (const svc of ['media', 'tools', 'games', 'wiki', 'blog', 'news', 'reviews', 'deals', 'coupons', 'trade', 'codes', 'space']) {const id = `${svc}.moderation.action`;const c = moderation.find(x => x.id === id);ok(c && c.owner === svc && c.adr === 'ADR-022', `${id} is owned by ${svc}`);nnew file mode 100644@@@ -0,0 +1,55 @@++'use strict';++const { test } = require('node:test');++const assert = require('node:assert/strict');++const fs = require('node:fs');++const path = require('node:path');++const contracts = require('..');++const root = path.join(__dirname, '..');++const fixture = (id, kind, name) => JSON.parse(fs.readFileSync(path.join(root, 'fixtures', id, kind, name), 'utf8'));++++test('Space launch serves its grants and retires Community forum grants', () => {++ const space = contracts.services.get('space');++ const community = contracts.services.get('community');++ assert.equal(space.exposure.state, 'live');++ assert.equal(space.exposure.publicSite, 'service');++ assert.deepEqual(space.capabilities, ['space.forum.read', 'space.forum.manage', 'space.thread.read', 'space.post.write']);++ assert.equal(contracts.capabilities.get('space.forum.manage').status, 'active');++ assert.ok(contracts.capabilities.get('space.post.write').implementedBy.includes('POST /api/v1/spaces/:space/threads'));++ for (const id of ['space.thread.write', 'space.pulse.read']) {++ assert.equal(contracts.capabilities.get(id).status, 'planned');++ assert.ok(!space.capabilities.includes(id));++ }++ for (const id of ['community.space.read', 'community.space.manage', 'community.thread.read', 'community.post.create']) {++ assert.equal(contracts.capabilities.get(id).status, 'retired');++ assert.ok(!community.capabilities.includes(id));++ assert.equal(contracts.capabilities.check({ cap: [id] }, id).code, 'capability.unknown');++ }++});++++test('Space event payloads validate and match the producer', () => {++ const space = contracts.services.get('space');++ for (const id of ['space.thread.created', 'space.post.created', 'space.moderation.action']) {++ assert.ok(space.eventsProduced.includes(id));++ assert.equal(contracts.catalog.find(c => c.id === id).owner, 'space');++ const dir = path.join(root, 'fixtures', id);++ for (const kind of ['valid', 'invalid']) for (const name of fs.readdirSync(path.join(dir, kind))) {++ const payload = fixture(id, kind, name);++ assert.equal(contracts.validate(`${id}@1`, payload).valid, kind === 'valid', `${id} ${kind}/${name}`);++ if (kind === 'valid') {++ const envelope = { event_id: 'evt_01JAB2C3D4E5F6G7H8J9K0MNPQ', event_type: id, version: 1, source: 'space', actor: { type: 'service', id: 'space' }, timestamp: '2026-10-07T00:00:00.000Z', subject: { type: 'thread', id: '1' }, payload };++ assert.equal(contracts.validate('events.event-envelope@1', envelope).valid, true, `${id} envelope`);++ }++ }++ }++ for (const id of ['community.thread.created', 'community.post.created']) {++ assert.equal(contracts.catalog.find(c => c.id === id).status, 'retired');++ assert.ok(!contracts.services.get('community').eventsProduced.includes(id));++ }++});++++test('Space blocked response uses the shared problem contract', () => {++ const blocked = fixture('errors.problem', 'valid', 'space-blocked.json');++ assert.equal(contracts.validate('errors.problem@1', blocked).valid, true);++ assert.equal(blocked.code, 'space.blocked');++ assert.equal(blocked.status, 403);++});
@@ -4,6 +4,31 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
@@ -4,6 +4,31 @@ 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.110.0 — 2026-10-07
+
+**OpenVibe.Space and OpenVibe.AI's public home launch** (plan T10 step 3, T6). It serves the forum at `https://openvibe.space` from
+loopback :4940. Community keeps pastes, comments, Pulse and submissions; its former `/s/*` forum paths
+redirect permanently to Space.
+- `manifests/services/space.json`: `status` alpha, `exposure` live with `publicSite: service`,
+ health and readiness paths, lifecycle, and the events Space produces and consumes.
+- `space.forum.read`, `space.forum.manage`, `space.thread.read`, and `space.post.write` are active with
+ Space's implemented routes. `space.post.write` still guards thread creation; `space.thread.write`
+ remains planned as a separate grant. `space.pulse.read` remains planned because Space serves no Pulse.
+- `space.thread.created`, `space.post.created`, and `space.moderation.action` have payload contracts and
+ valid and invalid fixtures. Space's source is `space`; `space.moderation.action` is
+ `common.moderation-action@1` (ADR-022) and OpenVibe.Network consumes it into the moderation audit log.
+- Space's problem codes, including `403 space.blocked`, are documented in
+ `docs/space-problem-codes.md`; the problem fixture demonstrates the stable URI and code.
+- The four former Community forum capabilities are retired and removed from its service list. The seven
+ Community forum schemas remain loadable but retired, with Space replacements and a compatibility
+ window through 1.0.0. Historical `community.thread.created` and `community.post.created` payloads
+ also remain loadable as retired types; Community no longer claims to produce them. Community's
+ planned vote grant now describes its surviving comment route only.
+- `manifests/services/ai.json`: `exposure` live with `publicSite: service`; `publicOrigin` is
+ `https://ai.openvibe.services` (the home, `/stats` and the developer-app API, AI #23), and
+ `ai.openvibe.network`, still listed in `domains`, answers 301 to it.
+- Generated bundles, types, and OpenAPI documents are refreshed for 0.110.0.
+
## 0.109.0 — 2026-10-07
## 0.109.0 — 2026-10-07
**OpenVibe.Run runs, OpenVibe.Services registers, and Bot learns buttons and points.** Two schemas gained optional
**OpenVibe.Run runs, OpenVibe.Services registers, and Bot learns buttons and points.** Two schemas gained optional
@@ -21,6 +21,13 @@
@@ -21,6 +21,13 @@
"inputSchema",
"inputSchema",
"outputSchema"
"outputSchema"
]
]
+ },
+ {
+ "id": "space.forum.manage",
+ "missing": [
+ "inputSchema",
+ "outputSchema"
+ ]
}
}
]
]
}
}
@@ -5,50 +5,64 @@
@@ -5,50 +5,64 @@
"id": "community.forum-space",
"id": "community.forum-space",
"replacement": "space.forum-space@1",
"replacement": "space.forum-space@1",
"since": "0.82.0",
"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)"
+ "removeAfter": "1.0.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); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"
},
},
{
{
"id": "community.forum-thread",
"id": "community.forum-thread",
"replacement": "space.forum-thread@1",
"replacement": "space.forum-thread@1",
"since": "0.82.0",
"since": "0.82.0",
- "removeAfter": "0.84.0",
- "reason": "the forum data moves to OpenVibe.Space (plan T10, D1/D3)"
+ "removeAfter": "1.0.0",
+ "reason": "the forum data moves to OpenVibe.Space (plan T10, D1/D3); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"
},
},
{
{
"id": "community.forum-post",
"id": "community.forum-post",
"replacement": "space.forum-post@1",
"replacement": "space.forum-post@1",
"since": "0.82.0",
"since": "0.82.0",
- "removeAfter": "0.84.0",
- "reason": "the forum data moves to OpenVibe.Space (plan T10, D1/D3)"
+ "removeAfter": "1.0.0",
+ "reason": "the forum data moves to OpenVibe.Space (plan T10, D1/D3); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"
},
},
{
{
"id": "community.space-read-result",
"id": "community.space-read-result",
"replacement": "space.forum-read-result@1",
"replacement": "space.forum-read-result@1",
"since": "0.82.0",
"since": "0.82.0",
- "removeAfter": "0.84.0",
- "reason": "community.space.read is renamed space.forum.read (plan T10, D3)"
+ "removeAfter": "1.0.0",
+ "reason": "community.space.read is renamed space.forum.read (plan T10, D3); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"
},
},
{
{
"id": "community.thread-read-result",
"id": "community.thread-read-result",
"replacement": "space.thread-read-result@1",
"replacement": "space.thread-read-result@1",
"since": "0.82.0",
"since": "0.82.0",
- "removeAfter": "0.84.0",
- "reason": "community.thread.read is renamed space.thread.read (plan T10, D3)"
+ "removeAfter": "1.0.0",
+ "reason": "community.thread.read is renamed space.thread.read (plan T10, D3); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"
},
},
{
{
"id": "community.post-write-request",
"id": "community.post-write-request",
"replacement": "space.post-write-request@1",
"replacement": "space.post-write-request@1",
"since": "0.82.0",
"since": "0.82.0",
- "removeAfter": "0.84.0",
- "reason": "community.post.create is renamed space.post.write (plan T10, D3)"
+ "removeAfter": "1.0.0",
+ "reason": "community.post.create is renamed space.post.write (plan T10, D3); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"
},
},
{
{
"id": "community.post-write-result",
"id": "community.post-write-result",
"replacement": "space.post-write-result@1",
"replacement": "space.post-write-result@1",
"since": "0.82.0",
"since": "0.82.0",
- "removeAfter": "0.84.0",
- "reason": "community.post.create is renamed space.post.write (plan T10, D3)"
+ "removeAfter": "1.0.0",
+ "reason": "community.post.create is renamed space.post.write (plan T10, D3); retired in 0.110.0 when Space launched, kept loadable for historical data and compatibility"
+ },
+ {
+ "id": "community.thread.created",
+ "replacement": "space.thread.created@1",
+ "since": "0.110.0",
+ "removeAfter": "1.0.0",
+ "reason": "Community forum producer retired after the move to OpenVibe.Space; historical payload remains loadable"
+ },
+ {
+ "id": "community.post.created",
+ "replacement": "space.post.created@1",
+ "since": "0.110.0",
+ "removeAfter": "1.0.0",
+ "reason": "Community forum producer retired after the move to OpenVibe.Space; historical payload remains loadable"
}
}
]
]
}
}
@@ -4590,7 +4590,7 @@
@@ -4590,7 +4590,7 @@
"schema": "events/payloads/community.thread.created.v1.json",
"schema": "events/payloads/community.thread.created.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
- "status": "active"
+ "status": "retired"
},
},
{
{
"id": "community.post.created",
"id": "community.post.created",
@@ -4599,7 +4599,7 @@
@@ -4599,7 +4599,7 @@
"schema": "events/payloads/community.post.created.v1.json",
"schema": "events/payloads/community.post.created.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
- "status": "active"
+ "status": "retired"
},
},
{
{
"id": "community.comment.created",
"id": "community.comment.created",
@@ -5007,7 +5007,7 @@
@@ -5007,7 +5007,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": "deprecated"
+ "status": "retired"
},
},
{
{
"id": "community.pulse-write-result",
"id": "community.pulse-write-result",
@@ -5025,7 +5025,7 @@
@@ -5025,7 +5025,7 @@
"schema": "community/forum-post.v1.json",
"schema": "community/forum-post.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
- "status": "deprecated"
+ "status": "retired"
},
},
{
{
"id": "community.forum-thread",
"id": "community.forum-thread",
@@ -5034,7 +5034,7 @@
@@ -5034,7 +5034,7 @@
"schema": "community/forum-thread.v1.json",
"schema": "community/forum-thread.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
- "status": "deprecated"
+ "status": "retired"
},
},
{
{
"id": "community.paste-create-result",
"id": "community.paste-create-result",
@@ -5052,7 +5052,7 @@
@@ -5052,7 +5052,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": "deprecated"
+ "status": "retired"
},
},
{
{
"id": "community.pulse-write-request",
"id": "community.pulse-write-request",
@@ -5106,7 +5106,7 @@
@@ -5106,7 +5106,7 @@
"schema": "community/forum-space.v1.json",
"schema": "community/forum-space.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
- "status": "deprecated"
+ "status": "retired"
},
},
{
{
"id": "community.pulse-item",
"id": "community.pulse-item",
@@ -5133,7 +5133,7 @@
@@ -5133,7 +5133,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": "deprecated"
+ "status": "retired"
},
},
{
{
"id": "community.thread-read-result",
"id": "community.thread-read-result",
@@ -5142,7 +5142,7 @@
@@ -5142,7 +5142,7 @@
"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"
+ "status": "retired"
},
},
{
{
"id": "space.forum-post",
"id": "space.forum-post",
@@ -6229,6 +6229,34 @@
@@ -6229,6 +6229,34 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active",
"status": "active",
"adr": "ADR-034"
"adr": "ADR-034"
+ },
+ {
+ "id": "space.thread.created",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "events/payloads/space.thread.created.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.post.created",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "events/payloads/space.post.created.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.moderation.action",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "events/payloads/space.moderation.action.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active",
+ "adr": "ADR-022"
}
}
]
]
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.moderation.action.v1.json",
+ "title": "SpaceModerationActionPayload",
+ "description": "space.moderation.action v1 (ADR-022; OpenVibe.Space server/events.js moderationAction). A staff or space moderator action on someone else's forum content, or a space moderator assignment: deleting, locking or unlocking a thread, deleting a post, adding or removing a space moderator. Not a person acting on their own content. Written to Space's outbox in the transaction that performs the action; OpenVibe.Network keeps it in the moderation audit log. The payload is common.moderation-action@1. Envelope: subject { type: moderation_action, id: <target type>:<target id> }, visibility internal, actor the moderator. Never carries the content itself.",
+ "$ref": "../../common/moderation-action.v1.json"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.post.created.v1.json",
+ "title": "SpacePostCreatedPayload",
+ "description": "space.post.created v1 (OpenVibe.Space server/events.js, queued in the transaction of the change). A reply was posted in a forum thread. Never the body. Envelope: subject { type: post, id }, visibility public when the item is public else internal, priority low, actor the person or service:space.",
+ "type": "object",
+ "required": [
+ "post_id",
+ "thread_id",
+ "space",
+ "author",
+ "visibility",
+ "url"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "post_id": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "thread_id": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "space": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9-]{0,62}$"
+ },
+ "author": {
+ "oneOf": [
+ {
+ "$ref": "../../identity/subject-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "members",
+ "hidden"
+ ]
+ },
+ "url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500,
+ "description": "The public page, or null when the item is not public."
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.thread.created.v1.json",
+ "title": "SpaceThreadCreatedPayload",
+ "description": "space.thread.created v1 (OpenVibe.Space server/events.js, queued in the transaction of the change). A forum thread was started in a space. Envelope: subject { type: thread, id }, visibility public when the item is public else internal, priority low, actor the person or service:space.",
+ "type": "object",
+ "required": [
+ "thread_id",
+ "space",
+ "author",
+ "visibility",
+ "url"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "thread_id": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "space": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9-]{0,62}$"
+ },
+ "author": {
+ "oneOf": [
+ {
+ "$ref": "../../identity/subject-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "members",
+ "hidden"
+ ]
+ },
+ "url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500,
+ "description": "The public page, or null when the item is not public."
+ }
+ }
+}
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/space/post-write-request.v1.json",
"$id": "https://openvibe.network/contracts/space/post-write-request.v1.json",
"title": "SpacePostWriteRequest",
"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, …).",
+ "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, 403 space.blocked, …); see docs/space-problem-codes.md.",
"anyOf": [
"anyOf": [
{
{
"type": "object",
"type": "object",
new file mode 100644
new file mode 100644
@@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
+# OpenVibe.Space problem codes
+
+Space returns `errors.problem@1` (`application/problem+json`). Its `code` is stable and its default `type` is `https://openvibe.network/problems/<code>`. Space's `server/forum/service.js`, `server/identity/blocks.js`, `server/forum/routes.js`, and `server/events-consumer.js` issue these `space.*` codes:
+
+| Status | Code | Meaning |
+| --- | --- | --- |
+| 403 | `space.blocked` | The author or moderator target has a blocking relationship with the caller. This also refuses adding a space moderator when either person blocked the other. |
+| 404 | `space.not_found` | The space or requested parent space does not exist, or a staff space is hidden from the caller. |
+| 403 | `space.staff_only` | Only discussion staff can post in this space. |
+| 403 | `space.staff_threads` | Only staff can start a Roadmap thread. |
+| 400 | `space.invalid_name` | A space needs a valid name. |
+| 400 | `space.invalid_style` | The style must be `feed` or `forum`. |
+| 400 | `space.invalid_kind` | The kind must be `discussion`, `request`, or `roadmap`. |
+| 400 | `space.invalid_parent` | The parent creates a cycle or nests boards too deeply. |
+| 400 | `space.invalid_slug` | The requested slug is invalid or reserved. |
+| 409 | `space.slug_taken` | The requested slug already belongs to a space. |
+| 403 | `space.reactions_off` | Ratings are off in this space. |
+| 403 | `space.votes_off` | Votes are off in this space. |
+| 403 | `space.internal_only` | The Events delivery route only accepts local requests. |
+| 503 | `space.events_disabled` | The Events delivery secret is not configured. |
+| 401 | `space.bad_signature` | The Events delivery signature is invalid or outside its replay window. |
+| 400 | `space.bad_delivery` | The Events delivery body is malformed. |
+| 500 | `space.event_failed` | Processing an Events delivery failed; Events may retry it. |
+
+`community.blocked` remains a Community code for its surviving comment and paste routes. Space uses its own `space.blocked` namespace for forum operations.
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "type": "https://openvibe.network/problems/space.blocked",
+ "title": "Forbidden",
+ "status": 403,
+ "code": "space.blocked",
+ "detail": "One of you blocked the other"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+{
+ "action": "post.deleted",
+ "target": {
+ "type": "post",
+ "id": "31"
+ },
+ "actor_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "content": "private body"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "action": "space.moderator_added",
+ "target": { "type": "space", "id": "general", "owner_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },
+ "actor_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "reason": null,
+ "details": {}
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "action": "space.moderator_removed",
+ "target": { "type": "space", "id": "general", "owner_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },
+ "actor_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "reason": null,
+ "details": {}
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
+{
+ "action": "thread.locked",
+ "target": {
+ "type": "thread",
+ "id": "31",
+ "owner_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR"
+ },
+ "actor_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "reason": null,
+ "details": {}
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
+{
+ "post_id": 31,
+ "thread_id": 7,
+ "space": "general",
+ "author": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "visibility": "public",
+ "url": "https://openvibe.space/s/general/7#p31",
+ "body": "the content never travels"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
+{
+ "post_id": 31,
+ "thread_id": 7,
+ "space": "general",
+ "author": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "visibility": "public",
+ "url": "https://openvibe.space/s/general/7#p31"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
+{
+ "thread_id": 7,
+ "space": "general",
+ "author": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "visibility": "public",
+ "url": "https://openvibe.space/s/general/7",
+ "body": "the content never travels"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "thread_id": 7,
+ "space": "general",
+ "author": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "visibility": "public",
+ "url": "https://openvibe.space/s/general/7"
+}
@@ -49830,7 +49830,7 @@
@@ -49830,7 +49830,7 @@
"community.thread.created": {
"community.thread.created": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "active",
+ "status": "retired",
"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/events/payloads/community.thread.created.v1.json",
"$id": "https://openvibe.network/contracts/events/payloads/community.thread.created.v1.json",
@@ -49885,7 +49885,7 @@
@@ -49885,7 +49885,7 @@
"community.post.created": {
"community.post.created": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "active",
+ "status": "retired",
"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/events/payloads/community.post.created.v1.json",
"$id": "https://openvibe.network/contracts/events/payloads/community.post.created.v1.json",
@@ -56611,7 +56611,7 @@
@@ -56611,7 +56611,7 @@
"community.post-write-request": {
"community.post-write-request": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "deprecated",
+ "status": "retired",
"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",
@@ -56778,7 +56778,7 @@
@@ -56778,7 +56778,7 @@
"community.forum-post": {
"community.forum-post": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "deprecated",
+ "status": "retired",
"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",
@@ -56973,7 +56973,7 @@
@@ -56973,7 +56973,7 @@
"community.forum-thread": {
"community.forum-thread": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "deprecated",
+ "status": "retired",
"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",
@@ -57258,7 +57258,7 @@
@@ -57258,7 +57258,7 @@
"community.post-write-result": {
"community.post-write-result": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "deprecated",
+ "status": "retired",
"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",
@@ -57847,7 +57847,7 @@
@@ -57847,7 +57847,7 @@
"community.forum-space": {
"community.forum-space": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "deprecated",
+ "status": "retired",
"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",
@@ -58149,7 +58149,7 @@
@@ -58149,7 +58149,7 @@
"community.space-read-result": {
"community.space-read-result": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "deprecated",
+ "status": "retired",
"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",
@@ -58224,7 +58224,7 @@
@@ -58224,7 +58224,7 @@
"community.thread-read-result": {
"community.thread-read-result": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "community",
"owner": "community",
- "status": "deprecated",
+ "status": "retired",
"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",
@@ -59034,7 +59034,7 @@
@@ -59034,7 +59034,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/space/post-write-request.v1.json",
"$id": "https://openvibe.network/contracts/space/post-write-request.v1.json",
"title": "SpacePostWriteRequest",
"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, …).",
+ "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, 403 space.blocked, …); see docs/space-problem-codes.md.",
"anyOf": [
"anyOf": [
{
{
"type": "object",
"type": "object",
@@ -72453,6 +72453,133 @@
@@ -72453,6 +72453,133 @@
}
}
}
}
}
}
+ },
+ "space.thread.created": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.thread.created.v1.json",
+ "title": "SpaceThreadCreatedPayload",
+ "description": "space.thread.created v1 (OpenVibe.Space server/events.js, queued in the transaction of the change). A forum thread was started in a space. Envelope: subject { type: thread, id }, visibility public when the item is public else internal, priority low, actor the person or service:space.",
+ "type": "object",
+ "required": [
+ "thread_id",
+ "space",
+ "author",
+ "visibility",
+ "url"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "thread_id": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "space": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9-]{0,62}$"
+ },
+ "author": {
+ "oneOf": [
+ {
+ "$ref": "../../identity/subject-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "members",
+ "hidden"
+ ]
+ },
+ "url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500,
+ "description": "The public page, or null when the item is not public."
+ }
+ }
+ }
+ },
+ "space.post.created": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.post.created.v1.json",
+ "title": "SpacePostCreatedPayload",
+ "description": "space.post.created v1 (OpenVibe.Space server/events.js, queued in the transaction of the change). A reply was posted in a forum thread. Never the body. Envelope: subject { type: post, id }, visibility public when the item is public else internal, priority low, actor the person or service:space.",
+ "type": "object",
+ "required": [
+ "post_id",
+ "thread_id",
+ "space",
+ "author",
+ "visibility",
+ "url"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "post_id": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "thread_id": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "space": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9-]{0,62}$"
+ },
+ "author": {
+ "oneOf": [
+ {
+ "$ref": "../../identity/subject-ref.v1.json"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "visibility": {
+ "enum": [
+ "public",
+ "members",
+ "hidden"
+ ]
+ },
+ "url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500,
+ "description": "The public page, or null when the item is not public."
+ }
+ }
+ }
+ },
+ "space.moderation.action": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.moderation.action.v1.json",
+ "title": "SpaceModerationActionPayload",
+ "description": "space.moderation.action v1 (ADR-022; OpenVibe.Space server/events.js moderationAction). A staff or space moderator action on someone else's forum content, or a space moderator assignment: deleting, locking or unlocking a thread, deleting a post, adding or removing a space moderator. Not a person acting on their own content. Written to Space's outbox in the transaction that performs the action; OpenVibe.Network keeps it in the moderation audit log. The payload is common.moderation-action@1. Envelope: subject { type: moderation_action, id: <target type>:<target id> }, visibility internal, actor the moderator. Never carries the content itself.",
+ "$ref": "../../common/moderation-action.v1.json"
+ }
}
}
}
}
}
}
@@ -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.109.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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"
@@ -12,7 +12,7 @@
@@ -12,7 +12,7 @@
},
},
"servers": [
"servers": [
{
{
- "url": "https://ai.openvibe.network"
+ "url": "https://ai.openvibe.services"
}
}
],
],
"paths": {
"paths": {
@@ -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.109.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bot API",
"title": "OpenVibe.Bot API",
- "version": "0.109.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.Coupons API",
"title": "OpenVibe.Coupons API",
- "version": "0.109.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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"
@@ -4,7 +4,7 @@
@@ -4,7 +4,7 @@
{
{
"service": "ai",
"service": "ai",
"name": "OpenVibe.AI",
"name": "OpenVibe.AI",
- "origin": "https://ai.openvibe.network",
+ "origin": "https://ai.openvibe.services",
"operations": 43,
"operations": 43,
"capabilities": 8
"capabilities": 8
},
},
@@ -159,8 +159,8 @@
@@ -159,8 +159,8 @@
"service": "space",
"service": "space",
"name": "OpenVibe.Space",
"name": "OpenVibe.Space",
"origin": "https://openvibe.space",
"origin": "https://openvibe.space",
- "operations": 15,
- "capabilities": 3
+ "operations": 30,
+ "capabilities": 4
},
},
{
{
"service": "tips",
"service": "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.109.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Run API",
"title": "OpenVibe.Run API",
- "version": "0.109.0",
- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Services API",
"title": "OpenVibe.Services API",
- "version": "0.109.0",
- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Space API",
"title": "OpenVibe.Space API",
- "version": "0.109.0",
- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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"
@@ -139,9 +139,592 @@
@@ -139,9 +139,592 @@
}
}
}
}
},
},
+ "/api/v1/posts/{id}/reactions": {
+ "post": {
+ "operationId": "post_api_v1_posts_id_reactions",
+ "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/space-groups": {
+ "get": {
+ "operationId": "get_api_v1_space_groups",
+ "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/space-groups/{group}": {
+ "put": {
+ "operationId": "put_api_v1_space_groups_group",
+ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",
+ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",
+ "tags": [
+ "space.forum"
+ ],
+ "x-openvibe-capabilities": [
+ "space.forum.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "space-moderate",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.forum.manage"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "group",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/v1/spaces": {
"/api/v1/spaces": {
"get": {
"get": {
- "operationId": "get_api_v1_spaces",
+ "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"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_api_v1_spaces",
+ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",
+ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",
+ "tags": [
+ "space.forum"
+ ],
+ "x-openvibe-capabilities": [
+ "space.forum.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "space-moderate",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.forum.manage"
+ ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/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}/attachments": {
+ "post": {
+ "operationId": "post_api_v1_spaces_space_attachments",
+ "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}/categories": {
+ "get": {
+ "operationId": "get_api_v1_spaces_space_categories",
+ "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}/categories/{category}": {
+ "delete": {
+ "operationId": "delete_api_v1_spaces_space_categories_category",
+ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",
+ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",
+ "tags": [
+ "space.forum"
+ ],
+ "x-openvibe-capabilities": [
+ "space.forum.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "space-moderate",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.forum.manage"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "category",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_api_v1_spaces_space_categories_category",
+ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",
+ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",
+ "tags": [
+ "space.forum"
+ ],
+ "x-openvibe-capabilities": [
+ "space.forum.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "space-moderate",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.forum.manage"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "category",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/spaces/{space}/members-only": {
+ "put": {
+ "operationId": "put_api_v1_spaces_space_members_only",
+ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",
+ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",
+ "tags": [
+ "space.forum"
+ ],
+ "x-openvibe-capabilities": [
+ "space.forum.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "space-moderate",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.forum.manage"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/spaces/{space}/moderators": {
+ "get": {
+ "operationId": "get_api_v1_spaces_space_moderators",
"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).",
"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.",
"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": [
"tags": [
@@ -161,6 +744,16 @@
@@ -161,6 +744,16 @@
]
]
}
}
],
],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
"responses": {
"responses": {
"2XX": {
"2XX": {
"description": "Success",
"description": "Success",
@@ -185,25 +778,25 @@
@@ -185,25 +778,25 @@
}
}
}
}
},
},
- "/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.",
+ "/api/v1/spaces/{space}/moderators/{subject}": {
+ "delete": {
+ "operationId": "delete_api_v1_spaces_space_moderators_subject",
+ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",
+ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",
"tags": [
"tags": [
"space.forum"
"space.forum"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "space.forum.read"
+ "space.forum.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "public"
+ "internal"
],
],
- "x-openvibe-quota-class": "space-read",
+ "x-openvibe-quota-class": "space-moderate",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "space.forum.read"
+ "space.forum.manage"
]
]
}
}
],
],
@@ -215,18 +808,123 @@
@@ -215,18 +808,123 @@
"schema": {
"schema": {
"type": "string"
"type": "string"
}
}
+ },
+ {
+ "name": "subject",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
}
}
],
],
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success",
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
"content": {
"content": {
- "application/json": {
+ "application/problem+json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/space.forum-read-result.v1"
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_api_v1_spaces_space_moderators_subject",
+ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",
+ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",
+ "tags": [
+ "space.forum"
+ ],
+ "x-openvibe-capabilities": [
+ "space.forum.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "space-moderate",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.forum.manage"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "subject",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
}
}
}
}
}
}
+ }
+ }
+ }
+ },
+ "/api/v1/spaces/{space}/settings": {
+ "put": {
+ "operationId": "put_api_v1_spaces_space_settings",
+ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",
+ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",
+ "tags": [
+ "space.forum"
+ ],
+ "x-openvibe-capabilities": [
+ "space.forum.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "space-moderate",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.forum.manage"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -245,7 +943,7 @@
@@ -245,7 +943,7 @@
"get": {
"get": {
"operationId": "get_api_v1_spaces_space_threads",
"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).",
"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.",
+ "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.",
"tags": [
"tags": [
"space.thread"
"space.thread"
],
],
@@ -430,7 +1128,7 @@
@@ -430,7 +1128,7 @@
"get": {
"get": {
"operationId": "get_api_v1_spaces_space_threads_slug",
"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).",
"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.",
+ "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.",
"tags": [
"tags": [
"space.thread"
"space.thread"
],
],
@@ -490,6 +1188,80 @@
@@ -490,6 +1188,80 @@
}
}
}
}
},
},
+ "/api/v1/spaces/{space}/threads/{slug}/crosspost": {
+ "post": {
+ "operationId": "post_api_v1_spaces_space_threads_slug_crosspost",
+ "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}/posts": {
"/api/v1/spaces/{space}/threads/{slug}/posts": {
"post": {
"post": {
"operationId": "post_api_v1_spaces_space_threads_slug_posts",
"operationId": "post_api_v1_spaces_space_threads_slug_posts",
@@ -564,6 +1336,63 @@
@@ -564,6 +1336,63 @@
}
}
}
}
},
},
+ "/api/v1/spaces/{space}/threads/{slug}/state": {
+ "put": {
+ "operationId": "put_api_v1_spaces_space_threads_slug_state",
+ "summary": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members.",
+ "description": "**space.forum.manage** (internal): Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",
+ "tags": [
+ "space.forum"
+ ],
+ "x-openvibe-capabilities": [
+ "space.forum.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "space-moderate",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.forum.manage"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "space",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/v1/spaces/{space}/threads/{slug}/votes": {
"/api/v1/spaces/{space}/threads/{slug}/votes": {
"post": {
"post": {
"operationId": "post_api_v1_spaces_space_threads_slug_votes",
"operationId": "post_api_v1_spaces_space_threads_slug_votes",
@@ -688,7 +1517,7 @@
@@ -688,7 +1517,7 @@
"get": {
"get": {
"operationId": "get_s_space",
"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).",
"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.",
+ "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.",
"tags": [
"tags": [
"space.thread"
"space.thread"
],
],
@@ -744,7 +1573,7 @@
@@ -744,7 +1573,7 @@
"get": {
"get": {
"operationId": "get_s_space_feed_xml",
"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).",
"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.",
+ "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.",
"tags": [
"tags": [
"space.thread"
"space.thread"
],
],
@@ -800,7 +1629,7 @@
@@ -800,7 +1629,7 @@
"get": {
"get": {
"operationId": "get_s_space_t_slug",
"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).",
"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.",
+ "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.",
"tags": [
"tags": [
"space.thread"
"space.thread"
],
],
@@ -864,7 +1693,7 @@
@@ -864,7 +1693,7 @@
"get": {
"get": {
"operationId": "get_s_feed_xml",
"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).",
"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.",
+ "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.",
"tags": [
"tags": [
"space.thread"
"space.thread"
],
],
@@ -1621,7 +2450,7 @@
@@ -1621,7 +2450,7 @@
},
},
"space.post-write-request.v1": {
"space.post-write-request.v1": {
"title": "SpacePostWriteRequest",
"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, …).",
+ "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, 403 space.blocked, …); see docs/space-problem-codes.md.",
"anyOf": [
"anyOf": [
{
{
"type": "object",
"type": "object",
@@ -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.109.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Watch API",
"title": "OpenVibe.Watch API",
- "version": "0.109.0",
- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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.109.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.109.0): each operation lists the capabilities it performs; a token for 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.110.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.110.0): each operation lists the capabilities it performs; a token for 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"
@@ -53417,7 +53417,7 @@ export interface SpaceForumThread {
@@ -53417,7 +53417,7 @@ export interface SpaceForumThread {
/** [email protected] (owner: space) */
/** [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, …).
+ * 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, 403 space.blocked, …); see docs/space-problem-codes.md.
*/
*/
export type SpacePostWriteRequest =
export type SpacePostWriteRequest =
| {
| {
@@ -58669,3 +58669,65 @@ export interface NetworkNodeRevokedPayload {
@@ -58669,3 +58669,65 @@ export interface NetworkNodeRevokedPayload {
*/
*/
at: string;
at: string;
}
}
+
+/** [email protected] (owner: space) */
+/**
+ * space.thread.created v1 (OpenVibe.Space server/events.js, queued in the transaction of the change). A forum thread was started in a space. Envelope: subject { type: thread, id }, visibility public when the item is public else internal, priority low, actor the person or service:space.
+ */
+export interface SpaceThreadCreatedPayload {
+ thread_id: number;
+ space: string;
+ author: SubjectRef | null;
+ visibility: "public" | "members" | "hidden";
+ /**
+ * The public page, or null when the item is not public.
+ */
+ url: string | null;
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.post.created v1 (OpenVibe.Space server/events.js, queued in the transaction of the change). A reply was posted in a forum thread. Never the body. Envelope: subject { type: post, id }, visibility public when the item is public else internal, priority low, actor the person or service:space.
+ */
+export interface SpacePostCreatedPayload {
+ post_id: number;
+ thread_id: number;
+ space: string;
+ author: SubjectRef | null;
+ visibility: "public" | "members" | "hidden";
+ /**
+ * The public page, or null when the item is not public.
+ */
+ url: string | null;
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.moderation.action v1 (ADR-022; OpenVibe.Space server/events.js moderationAction). A staff or space moderator action on someone else's forum content, or a space moderator assignment: deleting, locking or unlocking a thread, deleting a post, adding or removing a space moderator. Not a person acting on their own content. Written to Space's outbox in the transaction that performs the action; OpenVibe.Network keeps it in the moderation audit log. The payload is common.moderation-action@1. Envelope: subject { type: moderation_action, id: <target type>:<target id> }, visibility internal, actor the moderator. Never carries the content itself.
+ */
+export interface SpaceModerationActionPayload {
+ /**
+ * A short verb id, as the service names it: <thing>.<what happened> (post.hidden, user.banned, release.revoked, guard.blocked).
+ */
+ action: string;
+ target: {
+ /**
+ * What was acted on, in the service's own words (post, page, release, user, player, …).
+ */
+ type: string;
+ id: string;
+ /**
+ * Whose content or account it was, when known.
+ */
+ owner_subject?: string | null;
+ };
+ /**
+ * The staff member or moderator (null only for a service acting without a person).
+ */
+ actor_subject: string | null;
+ reason?: string | null;
+ /**
+ * Per action: the fields changed, the previous and new state, a bulk action's count, … never the content.
+ */
+ details?: {};
+}
@@ -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": "deprecated",
+ "status": "retired",
"visibility": "public",
"visibility": "public",
"sensitive": true,
"sensitive": true,
- "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.",
+ "description": "RETIRED at 0.110.0: OpenVibe.Community no longer serves this forum capability. Use space.post.write on OpenVibe.Space. Community /s/* paths redirect permanently to openvibe.space.",
"outputSchema": "community.post-write-result@1",
"outputSchema": "community.post-write-result@1",
"permissions": [
"permissions": [
"community:write"
"community:write"
@@ -14,16 +14,7 @@
@@ -14,16 +14,7 @@
"space_member"
"space_member"
],
],
"quotaClass": "community-write",
"quotaClass": "community-write",
- "events": [
- "community.thread.created",
- "community.post.created"
- ],
- "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"
- ],
+ "events": [],
+ "implementedBy": [],
"inputSchema": "community.post-write-request@1"
"inputSchema": "community.post-write-request@1"
}
}
@@ -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": "deprecated",
+ "status": "retired",
"visibility": "internal",
"visibility": "internal",
- "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.",
+ "description": "RETIRED at 0.110.0: OpenVibe.Community no longer serves this forum capability. Use space.forum.manage on OpenVibe.Space. Community /s/* paths redirect permanently to openvibe.space.",
"permissions": [
"permissions": [
"community:moderate"
"community:moderate"
],
],
@@ -13,7 +13,5 @@
@@ -13,7 +13,5 @@
],
],
"quotaClass": "community-moderate",
"quotaClass": "community-moderate",
"events": [],
"events": [],
- "implementedBy": [
- "PUT /api/v1/spaces/:space/members-only"
- ]
+ "implementedBy": []
}
}
@@ -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": "deprecated",
+ "status": "retired",
"visibility": "public",
"visibility": "public",
- "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.",
+ "description": "RETIRED at 0.110.0: OpenVibe.Community no longer serves this forum capability. Use space.forum.read on OpenVibe.Space. Community /s/* paths redirect permanently to openvibe.space.",
"outputSchema": "community.space-read-result@1",
"outputSchema": "community.space-read-result@1",
"permissions": [
"permissions": [
"community:read"
"community:read"
@@ -14,10 +14,6 @@
@@ -14,10 +14,6 @@
],
],
"quotaClass": "community-read",
"quotaClass": "community-read",
"events": [],
"events": [],
- "implementedBy": [
- "GET /api/v1/spaces",
- "GET /api/v1/spaces/:space",
- "GET /s"
- ],
+ "implementedBy": [],
"inputSchema": "common.no-body@1"
"inputSchema": "common.no-body@1"
}
}
@@ -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": "deprecated",
+ "status": "retired",
"visibility": "public",
"visibility": "public",
- "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.",
+ "description": "RETIRED at 0.110.0: OpenVibe.Community no longer serves this forum capability. Use space.thread.read on OpenVibe.Space. Community /s/* paths redirect permanently to openvibe.space.",
"outputSchema": "community.thread-read-result@1",
"outputSchema": "community.thread-read-result@1",
"permissions": [
"permissions": [
"community:read"
"community:read"
@@ -14,13 +14,6 @@
@@ -14,13 +14,6 @@
],
],
"quotaClass": "community-read",
"quotaClass": "community-read",
"events": [],
"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"
- ],
+ "implementedBy": [],
"inputSchema": "common.no-body@1"
"inputSchema": "common.no-body@1"
}
}
@@ -4,19 +4,17 @@
@@ -4,19 +4,17 @@
"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 space.post.write (threads) and community.comment.write (comments), which keep covering them.",
+ "description": "PLANNED as its own grant for Community comments: up-vote, down-vote or clear a vote (value 1, -1 or 0; 0 removes it). Community.comment.write guards the comment vote route today. Forum thread votes moved to OpenVibe.Space and are guarded there by space.post.write.",
"inputSchema": "common.entity-ref@1",
"inputSchema": "common.entity-ref@1",
"permissions": [
"permissions": [
"community:vote"
"community:vote"
],
],
"resourceConstraints": [
"resourceConstraints": [
- "space_member"
+ "none"
],
],
"quotaClass": "community-vote",
"quotaClass": "community-vote",
"events": [],
"events": [],
"implementedBy": [
"implementedBy": [
- "POST /api/v1/spaces/:space/threads/:slug/votes",
- "POST /api/v1/comments/:commentId/votes",
- "POST /s/:space/t/:slug/vote"
+ "POST /api/v1/comments/:commentId/votes"
]
]
}
}
@@ -2,9 +2,9 @@
@@ -2,9 +2,9 @@
"id": "space.forum.manage",
"id": "space.forum.manage",
"version": "1.0",
"version": "1.0",
"owner": "space",
"owner": "space",
- "status": "planned",
+ "status": "active",
"visibility": "internal",
"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.",
+ "description": "Manage forum spaces and moderation in OpenVibe.Space: create spaces, update settings and categories, manage moderators, and gate spaces to creator VIP members. Service callers need space.forum.manage; people need discussion staff or a space moderator where supported. PUT /api/v1/spaces/:space/moderators/:subject refuses a block in either direction with 403 space.blocked (errors.problem@1). Other Space problem codes are documented in docs/space-problem-codes.md.",
"permissions": [
"permissions": [
"space:moderate"
"space:moderate"
],
],
@@ -12,8 +12,16 @@
@@ -12,8 +12,16 @@
"none"
"none"
],
],
"quotaClass": "space-moderate",
"quotaClass": "space-moderate",
- "events": [],
+ "events": [
+ "space.moderation.action"
+ ],
"implementedBy": [
"implementedBy": [
- "PUT /api/v1/spaces/:space/members-only"
+ "POST /api/v1/spaces",
+ "PUT /api/v1/spaces/:space/settings",
+ "PUT /api/v1/spaces/:space/members-only",
+ "PUT|DELETE /api/v1/spaces/:space/categories/:category",
+ "PUT|DELETE /api/v1/spaces/:space/moderators/:subject",
+ "PUT /api/v1/spaces/:space/threads/:slug/state",
+ "PUT /api/v1/space-groups/:group"
]
]
}
}
@@ -17,7 +17,10 @@
@@ -17,7 +17,10 @@
"implementedBy": [
"implementedBy": [
"GET /api/v1/spaces",
"GET /api/v1/spaces",
"GET /api/v1/spaces/:space",
"GET /api/v1/spaces/:space",
- "GET /s"
+ "GET /s",
+ "GET /api/v1/spaces/:space/categories",
+ "GET /api/v1/spaces/:space/moderators",
+ "GET /api/v1/space-groups"
],
],
"inputSchema": "common.no-body@1"
"inputSchema": "common.no-body@1"
}
}
@@ -14,13 +14,19 @@
@@ -14,13 +14,19 @@
"space_member"
"space_member"
],
],
"quotaClass": "space-write",
"quotaClass": "space-write",
- "events": [],
+ "events": [
+ "space.thread.created",
+ "space.post.created"
+ ],
"implementedBy": [
"implementedBy": [
"POST /api/v1/spaces/:space/threads",
"POST /api/v1/spaces/:space/threads",
"POST /api/v1/spaces/:space/threads/:slug/posts",
"POST /api/v1/spaces/:space/threads/:slug/posts",
"POST /api/v1/spaces/:space/threads/:slug/votes",
"POST /api/v1/spaces/:space/threads/:slug/votes",
"PUT|DELETE /api/v1/posts/:id",
"PUT|DELETE /api/v1/posts/:id",
- "DELETE /api/v1/spaces/:space/threads/:slug"
+ "DELETE /api/v1/spaces/:space/threads/:slug",
+ "POST /api/v1/spaces/:space/attachments",
+ "POST /api/v1/spaces/:space/threads/:slug/crosspost",
+ "POST /api/v1/posts/:id/reactions"
],
],
"inputSchema": "space.post-write-request@1"
"inputSchema": "space.post-write-request@1"
}
}
@@ -4,7 +4,7 @@
@@ -4,7 +4,7 @@
"owner": "space",
"owner": "space",
"status": "active",
"status": "active",
"visibility": "public",
"visibility": "public",
- "description": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (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.",
+ "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.",
"outputSchema": "space.thread-read-result@1",
"outputSchema": "space.thread-read-result@1",
"permissions": [
"permissions": [
"space:read"
"space:read"
@@ -8,16 +8,17 @@
@@ -8,16 +8,17 @@
"status": "alpha",
"status": "alpha",
"repository": "OpenVibers/OpenVibe.AI",
"repository": "OpenVibers/OpenVibe.AI",
"domains": [
"domains": [
+ "ai.openvibe.services",
"ai.openvibe.network"
"ai.openvibe.network"
],
],
- "publicOrigin": "https://ai.openvibe.network",
+ "publicOrigin": "https://ai.openvibe.services",
"health": "/api/health",
"health": "/api/health",
"ready": "/api/ready",
"ready": "/api/ready",
"internalOrigin": "http://127.0.0.1:4700",
"internalOrigin": "http://127.0.0.1:4700",
"exposure": {
"exposure": {
- "state": "internal",
- "publicSite": null,
- "note": "called by other services at 127.0.0.1:4700; ai.openvibe.network serves the OpenVibe.Sites placeholder page"
+ "state": "live",
+ "publicSite": "service",
+ "note": "the public home, /stats and the developer-app API (ai.app.run) at ai.openvibe.services since 2026-10-07 (AI #23); ai.openvibe.network answers 301 to it; services on the host call 127.0.0.1:4700"
},
},
"capabilities": [
"capabilities": [
"ai.app.run",
"ai.app.run",
@@ -76,14 +77,14 @@
@@ -76,14 +77,14 @@
],
],
"window": "automatic only while ovhost waits for readiness after the restart (30 s in host.example.json); manual afterwards, as long as no blocker applies",
"window": "automatic only while ovhost waits for readiness after the restart (30 s in host.example.json); manual afterwards, as long as no blocker applies",
"blockers": {
"blockers": {
- "none": "the schema code only adds (CREATE … IF NOT EXISTS, ADD COLUMN; no DROP, RENAME or table rebuild), which an older release ignores"
+ "none": "the schema code only adds (CREATE \u2026 IF NOT EXISTS, ADD COLUMN; no DROP, RENAME or table rebuild), which an older release ignores"
}
}
},
},
"contracts": {
"contracts": {
"range": ">=0.6.0 <1.0.0"
"range": ">=0.6.0 <1.0.0"
},
},
"leases": {
"leases": {
- "none": "no claims: one process owns ai.db (better-sqlite3, single writer); a run is taken with a compare-and-set on its status (queued → running)"
+ "none": "no claims: one process owns ai.db (better-sqlite3, single writer); a run is taken with a compare-and-set on its status (queued \u2192 running)"
}
}
},
},
"notes": "Providers, models, routing profiles, versioned prompt templates and workflows, runs, request logs, citations, scoped cache, quotas and audit. Workflow output is a draft/evidence package attributed to workflow + model + run. ai.run.queued|succeeded|failed|cached have payload contracts (v0.30.0, status planned): AI publishes them once its Events client is wired in; it publishes none today. ai.run.create takes ai.run-request@1 and returns ai.run@1."
"notes": "Providers, models, routing profiles, versioned prompt templates and workflows, runs, request logs, citations, scoped cache, quotas and audit. Workflow output is a draft/evidence package attributed to workflow + model + run. ai.run.queued|succeeded|failed|cached have payload contracts (v0.30.0, status planned): AI publishes them once its Events client is wired in; it publishes none today. ai.run.create takes ai.run-request@1 and returns ai.run@1."
@@ -16,13 +16,9 @@
@@ -16,13 +16,9 @@
"community.paste.create",
"community.paste.create",
"community.paste.write",
"community.paste.write",
"community.paste.moderate",
"community.paste.moderate",
- "community.post.create",
"community.comment.write",
"community.comment.write",
"community.comment.moderate",
"community.comment.moderate",
"community.pulse.write",
"community.pulse.write",
- "community.space.read",
- "community.space.manage",
- "community.thread.read",
"community.vote.set",
"community.vote.set",
"community.pulse.read"
"community.pulse.read"
],
],
@@ -33,9 +29,7 @@
@@ -33,9 +29,7 @@
"community.moderation.action",
"community.moderation.action",
"community.paste.created",
"community.paste.created",
"community.paste.deleted",
"community.paste.deleted",
- "community.paste.updated",
- "community.post.created",
- "community.thread.created"
+ "community.paste.updated"
],
],
"eventsConsumed": [
"eventsConsumed": [
"network.account.deleted",
"network.account.deleted",
@@ -71,7 +65,7 @@
@@ -71,7 +65,7 @@
"drains": [
"drains": [
"Pulse subscription retries, the community.profile scan and the search-document scans stopped",
"Pulse subscription retries, the community.profile scan and the search-document scans stopped",
"stops taking connections; idle keep-alive connections closed (kept 65 s otherwise); requests in flight answered with Connection: close and allowed to finish (4 s at most, then cut)",
"stops taking connections; idle keep-alive connections closed (kept 65 s otherwise); requests in flight answered with Connection: close and allowed to finish (4 s at most, then cut)",
- "the profile scan in progress (it stops between people), the Discord relay's drain and the events outbox's send awaited; unsent rows stay in their tables",
+ "the profile scan in progress (it stops between people) and the events outbox's send awaited; unsent rows stay in their tables",
"community.db closed, exit 0"
"community.db closed, exit 0"
],
],
"note": "server/graceful.js; a stop that passes 5 s exits 1. Only the in-memory rate-limit sweeps end with the process. The unit sets no TimeoutStopSec (systemd's 90 s default applies)"
"note": "server/graceful.js; a stop that passes 5 s exits 1. Only the in-memory rate-limit sweeps end with the process. The unit sets no TimeoutStopSec (systemd's 90 s default applies)"
@@ -84,7 +78,7 @@
@@ -84,7 +78,7 @@
},
},
{
{
"kind": "schedule",
"kind": "schedule",
- "what": "the Roadmap space re-syncs from docs/roadmap/public.json; the community.profile and search-document scans start again from what changed"
+ "what": "the community.profile and search-document scans restart from what changed"
},
},
{
{
"kind": "consumer",
"kind": "consumer",
@@ -108,5 +102,6 @@
@@ -108,5 +102,6 @@
"leases": {
"leases": {
"none": "no claims: one process owns community.db (better-sqlite3, single writer)"
"none": "no claims: one process owns community.db (better-sqlite3, single writer)"
}
}
- }
+ },
+ "notes": "Community continues to serve pastes, comments, Pulse and submissions. Its former /s/* forum paths issue 301 redirects to openvibe.space; Space owns forum data and writes."
}
}
@@ -99,6 +99,7 @@
@@ -99,6 +99,7 @@
"media.moderation.action",
"media.moderation.action",
"news.moderation.action",
"news.moderation.action",
"reviews.moderation.action",
"reviews.moderation.action",
+ "space.moderation.action",
"tips.interaction.moderated",
"tips.interaction.moderated",
"tools.moderation.action",
"tools.moderation.action",
"tools.usage.recorded",
"tools.usage.recorded",
@@ -5,16 +5,15 @@
@@ -5,16 +5,15 @@
},
},
"id": "space",
"id": "space",
"name": "OpenVibe.Space",
"name": "OpenVibe.Space",
- "status": "placeholder",
+ "status": "alpha",
"repository": "OpenVibers/OpenVibe.Space",
"repository": "OpenVibers/OpenVibe.Space",
"domains": [
"domains": [
"openvibe.space"
"openvibe.space"
],
],
"publicOrigin": "https://openvibe.space",
"publicOrigin": "https://openvibe.space",
"exposure": {
"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"
+ "state": "live",
+ "publicSite": "service"
},
},
"site": {
"site": {
"name": "Space",
"name": "Space",
@@ -25,19 +24,19 @@
@@ -25,19 +24,19 @@
"position": 19,
"position": 19,
"tld": "space",
"tld": "space",
"accent": "#818cf8",
"accent": "#818cf8",
- "description": "OpenVibe.Space will be the forums of the OpenVibe network: create a forum for any topic, with the depth of classic boards like XenForo and phpBB, the feel of a modern community, and AI help for topics, research and moderation. The forums on OpenVibe.Community move here when it launches.",
+ "description": "OpenVibe.Space is the network forum: browse public and members-only boards, start threads, reply, vote and moderate spaces with one OpenVibe identity.",
"pillars": [
"pillars": [
{
{
"title": "Any forum",
"title": "Any forum",
- "text": "Anyone can start one, for any topic."
+ "text": "Browse boards for discussions, requests and the roadmap."
},
},
{
{
"title": "Classic depth",
"title": "Classic depth",
- "text": "Sub-forums, threads, ranks, moderation tools."
+ "text": "Child boards, threads, categories and moderation tools."
},
},
{
{
- "title": "AI assist",
- "text": "Topic ideas, research and summaries, always labelled."
+ "title": "Make it yours",
+ "text": "Moderators can shape boards, categories and visibility."
},
},
{
{
"title": "One account",
"title": "One account",
@@ -45,50 +44,80 @@
@@ -45,50 +44,80 @@
}
}
],
],
"keywords": "openvibe space, forums, create a forum, community boards, xenforo alternative, phpbb alternative",
"keywords": "openvibe space, forums, create a forum, community boards, xenforo alternative, phpbb alternative",
- "launch": "the forum engine moving over from OpenVibe.Community",
- "relationships": {
- "noRepo": true,
- "meanwhile": [
- {
- "label": "OpenVibe.Community",
- "url": "https://openvibe.community",
- "note": "Forums, pastes and people live there today."
- }
- ]
- }
+ "launch": "Forums moved from OpenVibe.Community to OpenVibe.Space (plan T10 step 3)."
},
},
"capabilities": [
"capabilities": [
"space.forum.read",
"space.forum.read",
"space.forum.manage",
"space.forum.manage",
"space.thread.read",
"space.thread.read",
- "space.thread.write",
- "space.post.write",
- "space.pulse.read"
+ "space.post.write"
+ ],
+ "eventsProduced": [
+ "space.thread.created",
+ "space.post.created",
+ "space.moderation.action"
+ ],
+ "eventsConsumed": [
+ "network.user.token_valid_after",
+ "network.block.changed",
+ "network.subject.merged",
+ "network.account.export_requested",
+ "network.account.deleted",
+ "vip.membership.changed"
],
],
- "eventsProduced": [],
- "eventsConsumed": [],
"namespacesOwned": [
"namespacesOwned": [
"space.*"
"space.*"
],
],
"lifecycle": {
"lifecycle": {
"liveness": {
"liveness": {
- "none": "planned service: no OpenVibe.Space process runs yet (plan T10 item 3)"
+ "endpoint": "/api/health",
+ "means": "the Space process answers HTTP; /api/ready checks PostgreSQL and reports degraded dependencies"
},
},
"shutdown": {
"shutdown": {
- "none": "planned service: nothing to stop yet"
+ "signal": "SIGTERM",
+ "deadlineSeconds": 5,
+ "drains": [
+ "subscription retries stop; the HTTP server stops accepting requests and gives in-flight requests up to 4 seconds",
+ "the Discord relay and Events outbox finish their current sends; unsent rows remain for the next start",
+ "the PostgreSQL pool closes and the process exits"
+ ],
+ "note": "server/index.js uses openvibe-sdk/service gracefulStop with a 4-second drain and 5-second deadline"
},
},
"startupRecovery": {
"startupRecovery": {
- "none": "planned service: nothing runs to resume"
+ "resumes": [
+ {
+ "kind": "outbox",
+ "what": "unsent space.* events in the PostgreSQL outbox relay after restart (at least once)"
+ },
+ {
+ "kind": "consumer",
+ "what": "Events subscriptions are recreated and pending network and VIP deliveries are retried idempotently"
+ },
+ {
+ "kind": "schedule",
+ "what": "the Roadmap space resyncs from docs/roadmap/public.json and Discord relay workers restart"
+ }
+ ]
},
},
"rollback": {
"rollback": {
- "none": "planned service: nothing is deployed to roll back"
+ "conditions": [
+ "automatic: ovhost deploy restores the prior sha if /api/ready fails in its readiness window",
+ "manual: ovhost rollback space --to <sha>"
+ ],
+ "window": "automatic during deploy readiness; manual afterwards while the previous code is database compatible",
+ "blockers": {
+ "none": "the initial PostgreSQL migration creates the Space schema without dropping earlier data"
+ }
},
},
"contracts": {
"contracts": {
"range": ">=0.82.0 <1.0.0"
"range": ">=0.82.0 <1.0.0"
},
},
"leases": {
"leases": {
- "none": "planned service: no process, no claims"
+ "none": "PostgreSQL outbox workers coordinate delivery; no separate service-owned lease is exposed"
}
}
},
},
- "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."
+ "notes": "Space owns forum spaces, threads, posts, votes, categories, reactions, moderation and the Discord relay in its PostgreSQL database. OpenVibe.Community retains pastes, comments, Pulse and submissions; legacy /s/* pages redirect permanently to openvibe.space.",
+ "health": "/api/health",
+ "ready": "/api/ready",
+ "internalOrigin": "http://127.0.0.1:4940"
}
}
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.109.0",
+ "version": "0.110.0",
"lockfileVersion": 3,
"lockfileVersion": 3,
"requires": true,
"requires": true,
"packages": {
"packages": {
"": {
"": {
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.109.0",
+ "version": "0.110.0",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.109.0",
+ "version": "0.110.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",
@@ -89,7 +89,8 @@ for (const cap of capabilities.manifests) {
@@ -89,7 +89,8 @@ for (const cap of capabilities.manifests) {
const r = contracts.validate('capabilities.capability@1', cap);
const r = contracts.validate('capabilities.capability@1', cap);
ok(r.valid, `capability ${cap.id}: ${JSON.stringify(r.errors)}`);
ok(r.valid, `capability ${cap.id}: ${JSON.stringify(r.errors)}`);
ok(serviceIds.has(cap.owner), `${cap.id} owner ${cap.owner} is a registered service`);
ok(serviceIds.has(cap.owner), `${cap.id} owner ${cap.owner} is a registered service`);
- ok(services.get(cap.owner).capabilities.includes(cap.id), `${cap.owner} manifest lists ${cap.id}`);
+ ok(['planned', 'retired'].includes(cap.status) || services.get(cap.owner).capabilities.includes(cap.id), `${cap.owner} manifest lists served ${cap.id}`);
+ if (cap.status === 'retired') ok(!services.get(cap.owner).capabilities.includes(cap.id), `${cap.owner} no longer offers retired ${cap.id}`);
for (const k of ['inputSchema', 'outputSchema']) if (cap[k]) ok(contracts.resolve(cap[k]), `${cap.id} ${k} resolves`);
for (const k of ['inputSchema', 'outputSchema']) if (cap[k]) ok(contracts.resolve(cap[k]), `${cap.id} ${k} resolves`);
ok(cap.status !== 'active' || cap.implementedBy.length > 0, `active ${cap.id} names the route that implements it`);
ok(cap.status !== 'active' || cap.implementedBy.length > 0, `active ${cap.id} names the route that implements it`);
}
}
@@ -121,8 +122,9 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
@@ -121,8 +122,9 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
const major = Number(c.version.split('.')[0]);
const major = Number(c.version.split('.')[0]);
ok(c.schema === `events/payloads/${c.id}.v${major}.json`, `${c.id} lives at events/payloads/<event_type>.v<major>.json`);
ok(c.schema === `events/payloads/${c.id}.v${major}.json`, `${c.id} lives at events/payloads/<event_type>.v<major>.json`);
ok(/^[a-z][a-z0-9_]*(\.[a-z0-9_]+){2,}$/.test(c.id), `${c.id} is an event type`);
ok(/^[a-z][a-z0-9_]*(\.[a-z0-9_]+){2,}$/.test(c.id), `${c.id} is an event type`);
- ok(services.get(c.owner) && services.get(c.owner).eventsProduced.includes(c.id), `${c.owner} manifest lists ${c.id} in eventsProduced`);
- ok(['active', 'planned'].includes(c.status), `${c.id} status ${c.status}`);
+ ok(services.get(c.owner) && (c.status === 'retired' || services.get(c.owner).eventsProduced.includes(c.id)), `${c.owner} manifest lists current ${c.id} in eventsProduced`);
+ ok(['active', 'planned', 'retired'].includes(c.status), `${c.id} status ${c.status}`);
+ if (c.status === 'retired') ok(!services.get(c.owner).eventsProduced.includes(c.id), `${c.owner} no longer produces retired ${c.id}`);
const s = contracts.schema(c.id);
const s = contracts.schema(c.id);
ok((s.type === 'object' || s.$ref || s.allOf) && s.title.endsWith('Payload'), `${c.id} is an object payload schema`);
ok((s.type === 'object' || s.$ref || s.allOf) && s.title.endsWith('Payload'), `${c.id} is an object payload schema`);
if (s.properties && s.properties.redacts) ok(s.properties.redacts.$ref === '../redaction-directive.v1.json', `${c.id} redacts uses events.redaction-directive@1`);
if (s.properties && s.properties.redacts) ok(s.properties.redacts.$ref === '../redaction-directive.v1.json', `${c.id} redacts uses events.redaction-directive@1`);
@@ -466,7 +468,7 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
@@ -466,7 +468,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.vote.set': 'planned', 'community.pulse.read': 'active', 'space.forum.read': 'active', 'space.forum.manage': 'planned', 'space.thread.read': 'active',
+ 'community.vote.set': 'planned', 'community.pulse.read': 'active', 'space.forum.read': 'active', '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)) {
@@ -475,9 +477,13 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
@@ -475,9 +477,13 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
if (status === 'planned') ok(c.description.startsWith('PLANNED'), `${id}: a planned capability says so first`);
if (status === 'planned') ok(c.description.startsWith('PLANNED'), `${id}: a planned capability says so first`);
if (status === 'active') ok(c.visibility === 'public' && c.implementedBy.every(r => r.startsWith('GET ')), `${id}: an active §30.2 read is a public GET`);
if (status === 'active') ok(c.visibility === 'public' && c.implementedBy.every(r => r.startsWith('GET ')), `${id}: an active §30.2 read is a public GET`);
}
}
+ ok(capabilities.get('space.forum.manage').status === 'active' && capabilities.get('space.forum.manage').implementedBy.includes('POST /api/v1/spaces'), 'space.forum.manage is served by Space');
+ ok(capabilities.get('space.thread.write').status === 'planned' && !services.get('space').capabilities.includes('space.thread.write'), 'space.thread.write is not a separate grant for the existing create route');
+ ok(capabilities.get('space.pulse.read').status === 'planned' && !services.get('space').capabilities.includes('space.pulse.read'), 'Space does not serve Pulse');
+ for (const id of ['community.space.read', 'community.space.manage', 'community.thread.read', 'community.post.create']) ok(capabilities.get(id).status === 'retired' && !services.get('community').capabilities.includes(id), `${id} retired from Community`);
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(/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(/space\.post\.write/i.test(capabilities.get('community.vote.set').description) && /community\.comment\.write/i.test(capabilities.get('community.vote.set').description) && capabilities.get('community.vote.set').implementedBy.length === 1, 'community.vote.set retains only the comment route');
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');
@@ -799,7 +805,7 @@ await assert.rejects(failing.getToken(), /401: invalid_client/);
@@ -799,7 +805,7 @@ await assert.rejects(failing.getToken(), /401: invalid_client/);
// without an older shape are common.moderation-action@1, and Network consumes every one of them.
// without an older shape are common.moderation-action@1, and Network consumes every one of them.
{
{
const moderation = contracts.catalog.filter(c => /\.moderation\.action$/.test(c.id));
const moderation = contracts.catalog.filter(c => /\.moderation\.action$/.test(c.id));
- for (const svc of ['media', 'tools', 'games', 'wiki', 'blog', 'news', 'reviews', 'deals', 'coupons', 'trade', 'codes']) {
+ for (const svc of ['media', 'tools', 'games', 'wiki', 'blog', 'news', 'reviews', 'deals', 'coupons', 'trade', 'codes', 'space']) {
const id = `${svc}.moderation.action`;
const id = `${svc}.moderation.action`;
const c = moderation.find(x => x.id === id);
const c = moderation.find(x => x.id === id);
ok(c && c.owner === svc && c.adr === 'ADR-022', `${id} is owned by ${svc}`);
ok(c && c.owner === svc && c.adr === 'ADR-022', `${id} is owned by ${svc}`);
new file mode 100644
new file mode 100644
@@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
+'use strict';
+const { test } = require('node:test');
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const path = require('node:path');
+const contracts = require('..');
+const root = path.join(__dirname, '..');
+const fixture = (id, kind, name) => JSON.parse(fs.readFileSync(path.join(root, 'fixtures', id, kind, name), 'utf8'));
+
+test('Space launch serves its grants and retires Community forum grants', () => {
+ const space = contracts.services.get('space');
+ const community = contracts.services.get('community');
+ assert.equal(space.exposure.state, 'live');
+ assert.equal(space.exposure.publicSite, 'service');
+ assert.deepEqual(space.capabilities, ['space.forum.read', 'space.forum.manage', 'space.thread.read', 'space.post.write']);
+ assert.equal(contracts.capabilities.get('space.forum.manage').status, 'active');
+ assert.ok(contracts.capabilities.get('space.post.write').implementedBy.includes('POST /api/v1/spaces/:space/threads'));
+ for (const id of ['space.thread.write', 'space.pulse.read']) {
+ assert.equal(contracts.capabilities.get(id).status, 'planned');
+ assert.ok(!space.capabilities.includes(id));
+ }
+ for (const id of ['community.space.read', 'community.space.manage', 'community.thread.read', 'community.post.create']) {
+ assert.equal(contracts.capabilities.get(id).status, 'retired');
+ assert.ok(!community.capabilities.includes(id));
+ assert.equal(contracts.capabilities.check({ cap: [id] }, id).code, 'capability.unknown');
+ }
+});
+
+test('Space event payloads validate and match the producer', () => {
+ const space = contracts.services.get('space');
+ for (const id of ['space.thread.created', 'space.post.created', 'space.moderation.action']) {
+ assert.ok(space.eventsProduced.includes(id));
+ assert.equal(contracts.catalog.find(c => c.id === id).owner, 'space');
+ const dir = path.join(root, 'fixtures', id);
+ for (const kind of ['valid', 'invalid']) for (const name of fs.readdirSync(path.join(dir, kind))) {
+ const payload = fixture(id, kind, name);
+ assert.equal(contracts.validate(`${id}@1`, payload).valid, kind === 'valid', `${id} ${kind}/${name}`);
+ if (kind === 'valid') {
+ const envelope = { event_id: 'evt_01JAB2C3D4E5F6G7H8J9K0MNPQ', event_type: id, version: 1, source: 'space', actor: { type: 'service', id: 'space' }, timestamp: '2026-10-07T00:00:00.000Z', subject: { type: 'thread', id: '1' }, payload };
+ assert.equal(contracts.validate('events.event-envelope@1', envelope).valid, true, `${id} envelope`);
+ }
+ }
+ }
+ for (const id of ['community.thread.created', 'community.post.created']) {
+ assert.equal(contracts.catalog.find(c => c.id === id).status, 'retired');
+ assert.ok(!contracts.services.get('community').eventsProduced.includes(id));
+ }
+});
+
+test('Space blocked response uses the shared problem contract', () => {
+ const blocked = fixture('errors.problem', 'valid', 'space-blocked.json');
+ assert.equal(contracts.validate('errors.problem@1', blocked).valid, true);
+ assert.equal(blocked.code, 'space.blocked');
+ assert.equal(blocked.status, 403);
+});