openvibers/openvibe.contracts Commits

Contracts 0.118.0: the forum returns to Community; Deals goes live; product copy for the new sites (#65)

OP OpenVibers committed 8 Oct 2026 · 28cb4a2f48 · 62 files, +2660 −3501

* Contracts 0.118.0: the forum returns to OpenVibe.Community

Owner decision 2026-10-08: Space becomes code hosting and "spaces"; the vBulletin/phpBB-style
forum lives on openvibe.community again. The mirror image of 1e30593 (0.110.0), which moved it
the other way.

- community.space.read, community.space.manage, community.thread.read and community.post.create
  are active again with Community's routes; community.vote.set names its thread and comment vote
  routes again; community.space.manage is the recorded schema gap where space.forum.manage was.
- The seven community.* forum schemas and the community.thread.created / community.post.created
  payloads are active again. The ten space.* forum contracts and events are retired and kept
  loadable, and compatibility/deprecations.json names each Community replacement (since 0.118.0,
  through 1.0.0). The Community records those replacements had are gone.
- manifests/services/community.json lists the forum capabilities and events again, and its notes
  and lifecycle describe the forum and its Discord relay it serves.
  manifests/services/space.json keeps no forum capability, event or work: it serves a home, its
  former /s/* paths redirect permanently to openvibe.community, and code hosting and "spaces" are
  still to be built.
- manifests/services/quest.json counts the Community forum events again; network consumes
  community.moderation.action and no longer space.moderation.action.
- test/space-release.test.js is the reverse check; test/run.js follows it.
- generated/: bundle, types and OpenAPI refreshed for 0.118.0. Space ends with no active
  capability, so generated/openapi/space.json is gone.

* 0.118.0: Space consumes no events; Deals is live; product copy and FAQs for Food, Help, Work, Quest, Rent and Watch

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 39d8a353..a5481374 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -4,6 +4,40 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
++## 0.118.0 — 2026-10-08
++
++**The forum returns to OpenVibe.Community** (owner decision 2026-10-08: Space becomes code hosting and "spaces";
++the vBulletin/phpBB-style forum lives on openvibe.community again, plan T20). The mirror of 0.110.0, which moved it
++the other way.
++- `manifests/services/community.json`: the forum is Community's again — `community.space.read`,
++ `community.space.manage`, `community.thread.read` and `community.post.create` are back in its capability list,
++ `community.thread.created` and `community.post.created` back in its `eventsProduced`, and its notes, its Discord
++ relay drain and its Roadmap-space recovery describe the forum it serves.
++- Those four capabilities are active again with Community's routes (`GET /s`, `GET /s/:space`,
++ `GET /s/:space/t/:slug`, `GET /s/feed.xml`, `GET /api/v1/spaces/:space/threads`, `POST /api/v1/spaces/:space/threads`,
++ `PUT /api/v1/spaces/:space/members-only`, …). `community.vote.set` names its thread and comment vote routes again,
++ guarded by `community.post.create` and `community.comment.write`; `community.space.manage` is the one active grant
++ without schemas, so `compatibility/capability-schema-gaps.json` lists it where `space.forum.manage` used to be.
++- The seven `community.*` forum schemas and the two event payloads are active again with their fixtures. The `space.*`
++ ones (`space.forum-space/thread/post`, `space.forum-read-result`, `space.thread-read-result`,
++ `space.post-write-request/result`, `space.thread.created`, `space.post.created`, `space.moderation.action`) are
++ retired and kept loadable, and `compatibility/deprecations.json` names each Community replacement with a window
++ through 1.0.0. The Community records those replacements had are gone.
++- `manifests/services/space.json`: no forum capability or event, and the site block and notes say Space is code
++ hosting, with "spaces" (apps that use your OpenVibe account and the SDK) coming next, and that its former `/s/*`
++ paths redirect permanently to openvibe.community. Exposure stays live (Space serves a home); its lifecycle drops
++ the forum's work (Discord relay, Roadmap resync, events outbox) and keeps what runs.
++- `manifests/services/quest.json` counts `community.thread.created` and `community.post.created` again;
++ `manifests/services/network.json` consumes `community.moderation.action` and no longer `space.moderation.action`.
++- Space ends with no active capability, so `generated/openapi/space.json` is gone.
++- Generated bundles, types and OpenAPI documents are refreshed for 0.118.0.
++- `manifests/services/space.json` consumes no events: Space's account export and deletion hooks only ever touched the
++ forum tables (the forum's account data is Community's), so Network stops waiting for Space's part.
++- `manifests/services/deals.json`: exposure `live` with `publicSite: service` — OpenVibe.Deals serves openvibe.deals
++ (472 DealNews offers with their links kept verbatim and attributed, community votes and watches).
++- Product copy for the services that launched this week: OpenVibe.Food, Help, Work, Quest, Rent and Watch describe
++ what is live now and what comes next, and each gets the common questions OpenVibe.Help shows.
++
## 0.117.0 — 2026-10-08
**OpenVibe.Quest and OpenVibe.Rent become services; OpenVibe.Watch goes live with its public site** (plan T18 step 8,
ddiff --git a/compatibility/capability-schema-gaps.json b/compatibility/capability-schema-gaps.json
iindex a2db35f3..76bf3bb3 100644
---- a/compatibility/capability-schema-gaps.json
++++ b/compatibility/capability-schema-gaps.json
@@@ -23,7 +23,7 @@
]
},
{
-- "id": "space.forum.manage",
++ "id": "community.space.manage",
"missing": [
"inputSchema",
"outputSchema"
ddiff --git a/compatibility/deprecations.json b/compatibility/deprecations.json
iindex 3d6b7db9..f7567c7f 100644
---- a/compatibility/deprecations.json
++++ b/compatibility/deprecations.json
@@@ -2,67 +2,74 @@
"_about": "Deprecated contracts: { id, replacement, since, removeAfter, reason }. A deprecated contract stays loadable until removeAfter.",
"deprecations": [
{
-- "id": "community.forum-space",
-- "replacement": "space.forum-space@1",
-- "since": "0.82.0",
++ "id": "space.forum-space",
++ "replacement": "community.forum-space@1",
++ "since": "0.118.0",
"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"
++ "reason": "the forum returned to OpenVibe.Community (2026-10-08)"
},
{
-- "id": "community.forum-thread",
-- "replacement": "space.forum-thread@1",
-- "since": "0.82.0",
++ "id": "space.forum-thread",
++ "replacement": "community.forum-thread@1",
++ "since": "0.118.0",
"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"
++ "reason": "the forum returned to OpenVibe.Community (2026-10-08)"
},
{
-- "id": "community.forum-post",
-- "replacement": "space.forum-post@1",
-- "since": "0.82.0",
++ "id": "space.forum-post",
++ "replacement": "community.forum-post@1",
++ "since": "0.118.0",
"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"
++ "reason": "the forum returned to OpenVibe.Community (2026-10-08)"
},
{
-- "id": "community.space-read-result",
-- "replacement": "space.forum-read-result@1",
-- "since": "0.82.0",
++ "id": "space.forum-read-result",
++ "replacement": "community.space-read-result@1",
++ "since": "0.118.0",
"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"
++ "reason": "the forum returned to OpenVibe.Community (2026-10-08)"
},
{
-- "id": "community.thread-read-result",
-- "replacement": "space.thread-read-result@1",
-- "since": "0.82.0",
++ "id": "space.thread-read-result",
++ "replacement": "community.thread-read-result@1",
++ "since": "0.118.0",
"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"
++ "reason": "the forum returned to OpenVibe.Community (2026-10-08)"
},
{
-- "id": "community.post-write-request",
-- "replacement": "space.post-write-request@1",
-- "since": "0.82.0",
++ "id": "space.post-write-request",
++ "replacement": "community.post-write-request@1",
++ "since": "0.118.0",
"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"
++ "reason": "the forum returned to OpenVibe.Community (2026-10-08)"
},
{
-- "id": "community.post-write-result",
-- "replacement": "space.post-write-result@1",
-- "since": "0.82.0",
++ "id": "space.post-write-result",
++ "replacement": "community.post-write-result@1",
++ "since": "0.118.0",
"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"
++ "reason": "the forum returned to OpenVibe.Community (2026-10-08)"
},
{
-- "id": "community.thread.created",
-- "replacement": "space.thread.created@1",
-- "since": "0.110.0",
++ "id": "space.thread.created",
++ "replacement": "community.thread.created@1",
++ "since": "0.118.0",
"removeAfter": "1.0.0",
-- "reason": "Community forum producer retired after the move to OpenVibe.Space; historical payload remains loadable"
++ "reason": "the forum returned to OpenVibe.Community (2026-10-08)"
},
{
-- "id": "community.post.created",
-- "replacement": "space.post.created@1",
-- "since": "0.110.0",
++ "id": "space.post.created",
++ "replacement": "community.post.created@1",
++ "since": "0.118.0",
"removeAfter": "1.0.0",
-- "reason": "Community forum producer retired after the move to OpenVibe.Space; historical payload remains loadable"
++ "reason": "the forum returned to OpenVibe.Community (2026-10-08)"
++ },
++ {
++ "id": "space.moderation.action",
++ "replacement": "community.moderation.action@1",
++ "since": "0.118.0",
++ "removeAfter": "1.0.0",
++ "reason": "the forum returned to OpenVibe.Community (2026-10-08)"
},
{
"id": "codes.app-manifest",
ddiff --git a/contracts/catalog.json b/contracts/catalog.json
iindex 640203bd..4fcaf40e 100644
---- a/contracts/catalog.json
++++ b/contracts/catalog.json
@@@ -4646,7 +4646,7 @@
"schema": "events/payloads/community.thread.created.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "retired"
++ "status": "active"
},
{
"id": "community.post.created",
@@@ -4655,7 +4655,7 @@
"schema": "events/payloads/community.post.created.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "retired"
++ "status": "active"
},
{
"id": "community.comment.created",
@@@ -5063,7 +5063,7 @@
"schema": "community/post-write-request.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "retired"
++ "status": "active"
},
{
"id": "community.pulse-write-result",
@@@ -5081,7 +5081,7 @@
"schema": "community/forum-post.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "retired"
++ "status": "active"
},
{
"id": "community.forum-thread",
@@@ -5090,7 +5090,7 @@
"schema": "community/forum-thread.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "retired"
++ "status": "active"
},
{
"id": "community.paste-create-result",
@@@ -5108,7 +5108,7 @@
"schema": "community/post-write-result.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "retired"
++ "status": "active"
},
{
"id": "community.pulse-write-request",
@@@ -5162,7 +5162,7 @@
"schema": "community/forum-space.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "retired"
++ "status": "active"
},
{
"id": "community.pulse-item",
@@@ -5189,7 +5189,7 @@
"schema": "community/space-read-result.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "retired"
++ "status": "active"
},
{
"id": "community.thread-read-result",
@@@ -5198,7 +5198,7 @@
"schema": "community/thread-read-result.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "retired"
++ "status": "active"
},
{
"id": "space.forum-post",
@@@ -5207,7 +5207,7 @@
"schema": "space/forum-post.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "active"
++ "status": "retired"
},
{
"id": "space.forum-read-result",
@@@ -5216,7 +5216,7 @@
"schema": "space/forum-read-result.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "active"
++ "status": "retired"
},
{
"id": "space.forum-space",
@@@ -5225,7 +5225,7 @@
"schema": "space/forum-space.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "active"
++ "status": "retired"
},
{
"id": "space.forum-thread",
@@@ -5234,7 +5234,7 @@
"schema": "space/forum-thread.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "active"
++ "status": "retired"
},
{
"id": "space.post-write-request",
@@@ -5243,7 +5243,7 @@
"schema": "space/post-write-request.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "active"
++ "status": "retired"
},
{
"id": "space.post-write-result",
@@@ -5252,7 +5252,7 @@
"schema": "space/post-write-result.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "active"
++ "status": "retired"
},
{
"id": "space.thread-read-result",
@@@ -5261,7 +5261,7 @@
"schema": "space/thread-read-result.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "active"
++ "status": "retired"
},
{
"id": "openre.destination",
@@@ -6293,7 +6293,7 @@
"schema": "events/payloads/space.thread.created.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "active"
++ "status": "retired"
},
{
"id": "space.post.created",
@@@ -6302,7 +6302,7 @@
"schema": "events/payloads/space.post.created.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "active"
++ "status": "retired"
},
{
"id": "space.moderation.action",
@@@ -6311,7 +6311,7 @@
"schema": "events/payloads/space.moderation.action.v1.json",
"visibility": "first-party",
"compatibility": "backward",
-- "status": "active",
++ "status": "retired",
"adr": "ADR-022"
},
{
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex 6bc13f19..d63d76f8 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -50351,7 +50351,7 @@
"community.thread.created": {
"version": "1.0.0",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/events/payloads/community.thread.created.v1.json",
@@@ -50406,7 +50406,7 @@
"community.post.created": {
"version": "1.0.0",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/events/payloads/community.post.created.v1.json",
@@@ -57132,7 +57132,7 @@
"community.post-write-request": {
"version": "1.0.0",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/post-write-request.v1.json",
@@@ -57299,7 +57299,7 @@
"community.forum-post": {
"version": "1.0.0",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/forum-post.v1.json",
@@@ -57494,7 +57494,7 @@
"community.forum-thread": {
"version": "1.0.0",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/forum-thread.v1.json",
@@@ -57779,7 +57779,7 @@
"community.post-write-result": {
"version": "1.0.0",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/post-write-result.v1.json",
@@@ -58368,7 +58368,7 @@
"community.forum-space": {
"version": "1.0.0",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/forum-space.v1.json",
@@@ -58670,7 +58670,7 @@
"community.space-read-result": {
"version": "1.0.0",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/space-read-result.v1.json",
@@@ -58745,7 +58745,7 @@
"community.thread-read-result": {
"version": "1.0.0",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/thread-read-result.v1.json",
@@@ -58884,7 +58884,7 @@
"space.forum-post": {
"version": "1.0.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/space/forum-post.v1.json",
@@@ -59079,7 +59079,7 @@
"space.forum-read-result": {
"version": "1.0.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/space/forum-read-result.v1.json",
@@@ -59154,7 +59154,7 @@
"space.forum-space": {
"version": "1.0.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/space/forum-space.v1.json",
@@@ -59298,7 +59298,7 @@
"space.forum-thread": {
"version": "1.0.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/space/forum-thread.v1.json",
@@@ -59550,7 +59550,7 @@
"space.post-write-request": {
"version": "1.0.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/space/post-write-request.v1.json",
@@@ -59676,7 +59676,7 @@
"space.post-write-result": {
"version": "1.0.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/space/post-write-result.v1.json",
@@@ -59773,7 +59773,7 @@
"space.thread-read-result": {
"version": "1.0.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/space/thread-read-result.v1.json",
@@@ -73006,7 +73006,7 @@
"space.thread.created": {
"version": "1.0.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/events/payloads/space.thread.created.v1.json",
@@@ -73061,7 +73061,7 @@
"space.post.created": {
"version": "1.0.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/events/payloads/space.post.created.v1.json",
@@@ -73121,7 +73121,7 @@
"space.moderation.action": {
"version": "1.0.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/events/payloads/space.moderation.action.v1.json",
ddiff --git a/generated/openapi/actor.json b/generated/openapi/actor.json
iindex 38113599..fd0886de 100644
---- a/generated/openapi/actor.json
++++ b/generated/openapi/actor.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Actor API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/ai.json b/generated/openapi/ai.json
iindex 4a9ad66d..592b0c0b 100644
---- a/generated/openapi/ai.json
++++ b/generated/openapi/ai.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.AI API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/billing.json b/generated/openapi/billing.json
iindex fea3d55a..2e8ef3ae 100644
---- a/generated/openapi/billing.json
++++ b/generated/openapi/billing.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Billing API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/blog.json b/generated/openapi/blog.json
iindex f82070ca..8a1c9aa5 100644
---- a/generated/openapi/blog.json
++++ b/generated/openapi/blog.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Blog API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/bot.json b/generated/openapi/bot.json
iindex 769b7a30..775f86a3 100644
---- a/generated/openapi/bot.json
++++ b/generated/openapi/bot.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Bot API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/chat.json b/generated/openapi/chat.json
iindex acb5244d..97427cf4 100644
---- a/generated/openapi/chat.json
++++ b/generated/openapi/chat.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Chat API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/community.json b/generated/openapi/community.json
iindex d38c292a..41166112 100644
---- a/generated/openapi/community.json
++++ b/generated/openapi/community.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Community API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
@@@ -855,15 +855,18 @@
"delete": {
"operationId": "delete_api_v1_posts_id",
"summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
-- "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.",
++ "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.\n\n**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
"tags": [
-- "community.comment"
++ "community.comment",
++ "community.post"
],
"x-openvibe-capabilities": [
-- "community.comment.moderate"
++ "community.comment.moderate",
++ "community.post.create"
],
"x-openvibe-visibility": [
-- "internal"
++ "internal",
++ "public"
],
"x-openvibe-quota-class": "community-moderate",
"security": [
@@@ -871,10 +874,22 @@
"openvibeToken": [
"community.comment.moderate"
]
++ },
++ {
++ "openvibeToken": [
++ "community.post.create"
++ ]
}
],
"x-openvibe-input": {
-- "$ref": "#/components/schemas/community.moderation-request.v1"
++ "anyOf": [
++ {
++ "$ref": "#/components/schemas/community.moderation-request.v1"
++ },
++ {
++ "$ref": "#/components/schemas/community.post-write-request.v1"
++ }
++ ]
},
"parameters": [
{
@@@ -892,7 +907,14 @@
"content": {
"application/json": {
"schema": {
-- "$ref": "#/components/schemas/community.moderation-result.v1"
++ "anyOf": [
++ {
++ "$ref": "#/components/schemas/community.moderation-result.v1"
++ },
++ {
++ "$ref": "#/components/schemas/community.post-write-result.v1"
++ }
++ ]
}
}
}
@@@ -912,15 +934,18 @@
"put": {
"operationId": "put_api_v1_posts_id",
"summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
-- "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.",
++ "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.\n\n**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
"tags": [
-- "community.comment"
++ "community.comment",
++ "community.post"
],
"x-openvibe-capabilities": [
-- "community.comment.moderate"
++ "community.comment.moderate",
++ "community.post.create"
],
"x-openvibe-visibility": [
-- "internal"
++ "internal",
++ "public"
],
"x-openvibe-quota-class": "community-moderate",
"security": [
@@@ -928,6 +953,11 @@
"openvibeToken": [
"community.comment.moderate"
]
++ },
++ {
++ "openvibeToken": [
++ "community.post.create"
++ ]
}
],
"requestBody": {
@@@ -935,7 +965,14 @@
"content": {
"application/json": {
"schema": {
-- "$ref": "#/components/schemas/community.moderation-request.v1"
++ "anyOf": [
++ {
++ "$ref": "#/components/schemas/community.moderation-request.v1"
++ },
++ {
++ "$ref": "#/components/schemas/community.post-write-request.v1"
++ }
++ ]
}
}
}
@@@ -956,7 +993,14 @@
"content": {
"application/json": {
"schema": {
-- "$ref": "#/components/schemas/community.moderation-result.v1"
++ "anyOf": [
++ {
++ "$ref": "#/components/schemas/community.moderation-result.v1"
++ },
++ {
++ "$ref": "#/components/schemas/community.post-write-result.v1"
++ }
++ ]
}
}
}
@@@ -1308,42 +1352,77 @@
}
}
},
-- "/api/v1/spaces/{space}/threads/{slug}": {
-- "delete": {
-- "operationId": "delete_api_v1_spaces_space_threads_slug",
-- "summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
-- "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.",
++ "/api/v1/spaces": {
++ "get": {
++ "operationId": "get_api_v1_spaces",
++ "summary": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*).",
++ "description": "**community.space.read** (public): List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Community checks space visibility itself.",
"tags": [
-- "community.comment"
++ "community.space"
],
"x-openvibe-capabilities": [
-- "community.comment.moderate"
++ "community.space.read"
],
"x-openvibe-visibility": [
-- "internal"
++ "public"
],
-- "x-openvibe-quota-class": "community-moderate",
++ "x-openvibe-quota-class": "community-read",
"security": [
{
"openvibeToken": [
-- "community.comment.moderate"
++ "community.space.read"
]
}
],
-- "x-openvibe-input": {
-- "$ref": "#/components/schemas/community.moderation-request.v1"
-- },
-- "parameters": [
-- {
-- "name": "space",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.space-read-result.v1"
++ }
++ }
}
},
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/spaces/{space}": {
++ "get": {
++ "operationId": "get_api_v1_spaces_space",
++ "summary": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*).",
++ "description": "**community.space.read** (public): List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Community checks space visibility itself.",
++ "tags": [
++ "community.space"
++ ],
++ "x-openvibe-capabilities": [
++ "community.space.read"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "community-read",
++ "security": [
{
-- "name": "slug",
++ "openvibeToken": [
++ "community.space.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "space",
"in": "path",
"required": true,
"schema": {
@@@ -1357,7 +1436,7 @@
"content": {
"application/json": {
"schema": {
-- "$ref": "#/components/schemas/community.moderation-result.v1"
++ "$ref": "#/components/schemas/community.space-read-result.v1"
}
}
}
@@@ -1375,16 +1454,16 @@
}
}
},
-- "/api/v1/spaces/{space}/threads/{slug}/state": {
++ "/api/v1/spaces/{space}/members-only": {
"put": {
-- "operationId": "put_api_v1_spaces_space_threads_slug_state",
-- "summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
-- "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.",
++ "operationId": "put_api_v1_spaces_space_members_only",
++ "summary": "Staff administration of spaces: create, rename, set visibility, and gate a space to one creator's VIP members.",
++ "description": "**community.space.manage** (internal): Staff administration of spaces: create, rename, set visibility, and gate a space to one creator's VIP members. Only the members-only gate is served today (checked with community.comment.moderate plus a moderator); spaces are seeded by Community's database setup, and nothing creates or edits them.",
"tags": [
-- "community.comment"
++ "community.space"
],
"x-openvibe-capabilities": [
-- "community.comment.moderate"
++ "community.space.manage"
],
"x-openvibe-visibility": [
"internal"
@@@ -1393,20 +1472,10 @@
"security": [
{
"openvibeToken": [
-- "community.comment.moderate"
++ "community.space.manage"
]
}
],
-- "requestBody": {
-- "required": false,
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/community.moderation-request.v1"
-- }
-- }
-- }
-- },
"parameters": [
{
"name": "space",
@@@ -1415,9 +1484,50 @@
"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": {
++ "get": {
++ "operationId": "get_api_v1_spaces_space_threads",
++ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",
++ "description": "**community.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds. Community checks space visibility and the VIP gate (403 vip.members_only). Creating threads and replies stays community.post.create.",
++ "tags": [
++ "community.thread"
++ ],
++ "x-openvibe-capabilities": [
++ "community.thread.read"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "community-read",
++ "security": [
{
-- "name": "slug",
++ "openvibeToken": [
++ "community.thread.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "space",
"in": "path",
"required": true,
"schema": {
@@@ -1431,7 +1541,7 @@
"content": {
"application/json": {
"schema": {
-- "$ref": "#/components/schemas/community.moderation-result.v1"
++ "$ref": "#/components/schemas/community.thread-read-result.v1"
}
}
}
@@@ -1447,37 +1557,55 @@
}
}
}
-- }
-- },
-- "/pulse": {
-- "get": {
-- "operationId": "get_pulse",
-- "summary": "Read the network's public activity feed (Pulse): entity reference, title, URL and origin (user, ai or system), newest first with a cursor.",
-- "description": "**community.pulse.read** (public): Read the network's public activity feed (Pulse): entity reference, title, URL and origin (user, ai or system), newest first with a cursor. Only public items are ever stored. Served to anyone (CORS allow-list for browsers). Live's /api/home/pulse is a different, Live-only feed.",
++ },
++ "post": {
++ "operationId": "post_api_v1_spaces_space_threads",
++ "summary": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI",
++ "description": "**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
"tags": [
-- "community.pulse"
++ "community.post"
],
"x-openvibe-capabilities": [
-- "community.pulse.read"
++ "community.post.create"
],
"x-openvibe-visibility": [
"public"
],
-- "x-openvibe-quota-class": "community-read",
++ "x-openvibe-quota-class": "community-write",
"security": [
{
"openvibeToken": [
-- "community.pulse.read"
++ "community.post.create"
]
}
],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.post-write-request.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "space",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
"responses": {
"2XX": {
"description": "Success",
"content": {
"application/json": {
"schema": {
-- "$ref": "#/components/schemas/community.pulse-read-result.v1"
++ "$ref": "#/components/schemas/community.post-write-result.v1"
}
}
}
@@@ -1494,334 +1622,1568 @@
}
}
}
-- }
-- },
-- "components": {
-- "schemas": {
-- "common.entity-ref.v1": {
-- "title": "EntityRef",
-- "description": "Typed reference to another service's entity. Store this instead of a foreign key; resolve a display projection from the owner.",
-- "type": "object",
-- "required": [
-- "service",
-- "type",
-- "id"
++ },
++ "/api/v1/spaces/{space}/threads/{slug}": {
++ "delete": {
++ "operationId": "delete_api_v1_spaces_space_threads_slug",
++ "summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
++ "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.\n\n**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
++ "tags": [
++ "community.comment",
++ "community.post"
],
-- "additionalProperties": false,
-- "properties": {
-- "service": {
-- "type": "string",
-- "pattern": "^[a-z][a-z0-9-]{1,39}$"
-- },
-- "type": {
-- "type": "string",
-- "pattern": "^[a-z][a-z0-9_]{1,39}$"
-- },
-- "id": {
-- "type": "string",
-- "minLength": 1,
-- "maxLength": 128
-- },
-- "revision": {
-- "type": "integer",
-- "minimum": 0
++ "x-openvibe-capabilities": [
++ "community.comment.moderate",
++ "community.post.create"
++ ],
++ "x-openvibe-visibility": [
++ "internal",
++ "public"
++ ],
++ "x-openvibe-quota-class": "community-moderate",
++ "security": [
++ {
++ "openvibeToken": [
++ "community.comment.moderate"
++ ]
},
-- "label": {
-- "type": "string",
-- "maxLength": 200,
-- "description": "Cached display label. Never authoritative."
++ {
++ "openvibeToken": [
++ "community.post.create"
++ ]
++ }
++ ],
++ "x-openvibe-input": {
++ "anyOf": [
++ {
++ "$ref": "#/components/schemas/community.moderation-request.v1"
++ },
++ {
++ "$ref": "#/components/schemas/community.post-write-request.v1"
++ }
++ ]
++ },
++ "parameters": [
++ {
++ "name": "space",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "slug",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "anyOf": [
++ {
++ "$ref": "#/components/schemas/community.moderation-result.v1"
++ },
++ {
++ "$ref": "#/components/schemas/community.post-write-result.v1"
++ }
++ ]
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ },
++ "get": {
++ "operationId": "get_api_v1_spaces_space_threads_slug",
++ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",
++ "description": "**community.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds. Community checks space visibility and the VIP gate (403 vip.members_only). Creating threads and replies stays community.post.create.",
++ "tags": [
++ "community.thread"
++ ],
++ "x-openvibe-capabilities": [
++ "community.thread.read"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "community-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "community.thread.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "space",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "slug",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.thread-read-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/spaces/{space}/threads/{slug}/posts": {
++ "post": {
++ "operationId": "post_api_v1_spaces_space_threads_slug_posts",
++ "summary": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI",
++ "description": "**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
++ "tags": [
++ "community.post"
++ ],
++ "x-openvibe-capabilities": [
++ "community.post.create"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "community-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "community.post.create"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.post-write-request.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "space",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "slug",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.post-write-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/spaces/{space}/threads/{slug}/state": {
++ "put": {
++ "operationId": "put_api_v1_spaces_space_threads_slug_state",
++ "summary": "Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g.",
++ "description": "**community.comment.moderate** (internal): Moderation of discussions: set a comment thread's visibility (public, hidden, locked — e.g. when the owner service hides the entity), delete any comment or forum post, pin and lock forum threads, read hidden threads, administer the Discord relay. Acting as itself (no X-OV-Subject) the service is the moderator; acting for a person it must vouch with X-OV-Staff: 1 that the person is staff.",
++ "tags": [
++ "community.comment"
++ ],
++ "x-openvibe-capabilities": [
++ "community.comment.moderate"
++ ],
++ "x-openvibe-visibility": [
++ "internal"
++ ],
++ "x-openvibe-quota-class": "community-moderate",
++ "security": [
++ {
++ "openvibeToken": [
++ "community.comment.moderate"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.moderation-request.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "space",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "slug",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.moderation-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/spaces/{space}/threads/{slug}/votes": {
++ "post": {
++ "operationId": "post_api_v1_spaces_space_threads_slug_votes",
++ "summary": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI",
++ "description": "**community.post.create** (public): Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
++ "tags": [
++ "community.post"
++ ],
++ "x-openvibe-capabilities": [
++ "community.post.create"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "community-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "community.post.create"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.post-write-request.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "space",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "slug",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.post-write-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/pulse": {
++ "get": {
++ "operationId": "get_pulse",
++ "summary": "Read the network's public activity feed (Pulse): entity reference, title, URL and origin (user, ai or system), newest first with a cursor.",
++ "description": "**community.pulse.read** (public): Read the network's public activity feed (Pulse): entity reference, title, URL and origin (user, ai or system), newest first with a cursor. Only public items are ever stored. Served to anyone (CORS allow-list for browsers). Live's /api/home/pulse is a different, Live-only feed.",
++ "tags": [
++ "community.pulse"
++ ],
++ "x-openvibe-capabilities": [
++ "community.pulse.read"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "community-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "community.pulse.read"
++ ]
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.pulse-read-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/s": {
++ "get": {
++ "operationId": "get_s",
++ "summary": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*).",
++ "description": "**community.space.read** (public): List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Community checks space visibility itself.",
++ "tags": [
++ "community.space"
++ ],
++ "x-openvibe-capabilities": [
++ "community.space.read"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "community-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "community.space.read"
++ ]
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.space-read-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/s/{space}": {
++ "get": {
++ "operationId": "get_s_space",
++ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",
++ "description": "**community.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds. Community checks space visibility and the VIP gate (403 vip.members_only). Creating threads and replies stays community.post.create.",
++ "tags": [
++ "community.thread"
++ ],
++ "x-openvibe-capabilities": [
++ "community.thread.read"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "community-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "community.thread.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "space",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.thread-read-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/s/{space}/feed.xml": {
++ "get": {
++ "operationId": "get_s_space_feed_xml",
++ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",
++ "description": "**community.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds. Community checks space visibility and the VIP gate (403 vip.members_only). Creating threads and replies stays community.post.create.",
++ "tags": [
++ "community.thread"
++ ],
++ "x-openvibe-capabilities": [
++ "community.thread.read"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "community-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "community.thread.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "space",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.thread-read-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/s/{space}/t/{slug}": {
++ "get": {
++ "operationId": "get_s_space_t_slug",
++ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",
++ "description": "**community.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds. Community checks space visibility and the VIP gate (403 vip.members_only). Creating threads and replies stays community.post.create.",
++ "tags": [
++ "community.thread"
++ ],
++ "x-openvibe-capabilities": [
++ "community.thread.read"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "community-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "community.thread.read"
++ ]
++ }
++ ],
++ "parameters": [
++ {
++ "name": "space",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "slug",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.thread-read-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/s/feed.xml": {
++ "get": {
++ "operationId": "get_s_feed_xml",
++ "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds.",
++ "description": "**community.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds. Community checks space visibility and the VIP gate (403 vip.members_only). Creating threads and replies stays community.post.create.",
++ "tags": [
++ "community.thread"
++ ],
++ "x-openvibe-capabilities": [
++ "community.thread.read"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "community-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "community.thread.read"
++ ]
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/community.thread-read-result.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "components": {
++ "schemas": {
++ "common.entity-ref.v1": {
++ "title": "EntityRef",
++ "description": "Typed reference to another service's entity. Store this instead of a foreign key; resolve a display projection from the owner.",
++ "type": "object",
++ "required": [
++ "service",
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "service": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9-]{1,39}$"
++ },
++ "type": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9_]{1,39}$"
++ },
++ "id": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 128
++ },
++ "revision": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "label": {
++ "type": "string",
++ "maxLength": 200,
++ "description": "Cached display label. Never authoritative."
++ }
++ }
++ },
++ "common.no-body.v1": {
++ "title": "NoBody",
++ "description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",
++ "type": "object",
++ "maxProperties": 0
++ },
++ "community.comment-thread.v1": {
++ "title": "CommunityCommentThread",
++ "description": "community.comment-thread@1: one embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeThread): the entity it belongs to, its visibility and comment count. id is the sequential id for services and the access id (cth_…) for everyone else; a hidden thread shows moderators its count, others only that it is hidden.",
++ "type": "object",
++ "required": [
++ "id",
++ "access_id",
++ "ref",
++ "visibility",
++ "comment_count"
++ ],
++ "properties": {
++ "id": {
++ "type": [
++ "integer",
++ "string"
++ ]
++ },
++ "access_id": {
++ "type": "string",
++ "pattern": "^cth_"
++ },
++ "ref": {
++ "$ref": "#/components/schemas/common.entity-ref.v1"
++ },
++ "visibility": {
++ "enum": [
++ "public",
++ "hidden",
++ "locked"
++ ]
++ },
++ "comment_count": {
++ "type": [
++ "integer",
++ "null"
++ ]
++ },
++ "created_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ }
++ }
++ },
++ "community.comment-write-request.v1": {
++ "title": "CommunityCommentWriteRequest",
++ "description": "community.comment-write-request@1: bodies of community.comment.write on OpenVibe.Community (people, anonymous people with a name where the thread allows it, and services acting as X-OV-Subject or as AI). POST /api/v1/comments/threads/resolve: { ref } — get or create the thread of an entity (browsers only for the allow-listed entity types; a label is taken only from a service). POST /api/v1/comments/threads/:id/comments: { message, parent_id?, anon_name? } (message trimmed, at most 5000 characters; a reply to a reply joins the top-level comment). POST /api/v1/comments/:commentId/votes: { value: 1 | -1 | 0 } (people). GET /api/v1/comments/threads/:id and DELETE /api/v1/comments/:commentId take no body. Unknown fields are ignored.",
++ "anyOf": [
++ {
++ "type": "object",
++ "required": [
++ "ref"
++ ],
++ "properties": {
++ "ref": {
++ "$ref": "#/components/schemas/common.entity-ref.v1"
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "message"
++ ],
++ "properties": {
++ "message": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 5000,
++ "pattern": "\\S"
++ },
++ "parent_id": {
++ "type": [
++ "integer",
++ "string",
++ "null"
++ ]
++ },
++ "anon_name": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "value"
++ ],
++ "properties": {
++ "value": {
++ "enum": [
++ 1,
++ -1,
++ 0,
++ "1",
++ "-1",
++ "0"
++ ]
++ }
++ }
++ },
++ {
++ "$ref": "#/components/schemas/common.no-body.v1"
++ }
++ ]
++ },
++ "community.comment-write-result.v1": {
++ "title": "CommunityCommentWriteResult",
++ "description": "community.comment-write-result@1: answers of community.comment.write on OpenVibe.Community. POST …/threads/resolve → { thread, created } (201 when created); GET …/threads/:id?after=&sort=&limit=&parent= → { thread, comments, next_cursor, viewer }; POST …/threads/:id/comments → 201 { comment }; DELETE /api/v1/comments/:commentId → { ok, id }; POST …/:commentId/votes → { comment_id, score, upvotes, downvotes, my_vote }. Refusals are problem+json (thread.locked, auth.required, community.blocked, …).",
++ "anyOf": [
++ {
++ "type": "object",
++ "required": [
++ "thread",
++ "created"
++ ],
++ "properties": {
++ "thread": {
++ "$ref": "#/components/schemas/community.comment-thread.v1"
++ },
++ "created": {
++ "type": "boolean"
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "thread",
++ "comments",
++ "next_cursor",
++ "viewer"
++ ],
++ "properties": {
++ "thread": {
++ "$ref": "#/components/schemas/community.comment-thread.v1"
++ },
++ "comments": {
++ "type": "array",
++ "items": {
++ "$ref": "#/components/schemas/community.comment.v1"
++ }
++ },
++ "next_cursor": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "viewer": {
++ "type": "object",
++ "properties": {
++ "signed_in": {
++ "type": "boolean"
++ },
++ "can_comment": {
++ "type": "boolean"
++ },
++ "can_vote": {
++ "type": "boolean"
++ },
++ "can_moderate": {
++ "type": "boolean"
++ }
++ }
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "comment"
++ ],
++ "properties": {
++ "comment": {
++ "$ref": "#/components/schemas/community.comment.v1"
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "ok",
++ "id"
++ ],
++ "properties": {
++ "ok": {
++ "const": true
++ },
++ "id": {
++ "type": "integer"
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "comment_id",
++ "score",
++ "upvotes",
++ "downvotes",
++ "my_vote"
++ ],
++ "properties": {
++ "comment_id": {
++ "type": "integer"
++ },
++ "score": {
++ "type": "integer"
++ },
++ "upvotes": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "downvotes": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "my_vote": {
++ "enum": [
++ 1,
++ -1,
++ 0
++ ]
++ }
++ }
++ }
++ ]
++ },
++ "community.comment.v1": {
++ "title": "CommunityComment",
++ "description": "community.comment@1: one comment in an embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeComment), with its author (or anonymous name), score and the caller's vote, reply count and, for top-level comments in a page, the first replies. A deleted comment keeps its place with no author or message.",
++ "type": "object",
++ "required": [
++ "id",
++ "thread_id",
++ "parent_id",
++ "origin",
++ "author",
++ "message",
++ "deleted",
++ "score",
++ "my_vote",
++ "can_edit",
++ "can_delete"
++ ],
++ "properties": {
++ "id": {
++ "type": "integer"
++ },
++ "thread_id": {
++ "type": [
++ "integer",
++ "string"
++ ]
++ },
++ "parent_id": {
++ "type": [
++ "integer",
++ "null"
++ ]
++ },
++ "origin": {
++ "type": "string"
++ },
++ "author": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "properties": {
++ "subject": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "username": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "display_name": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "avatar_url": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "profile_color": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "is_ai": {
++ "const": true
++ },
++ "is_system": {
++ "const": true
++ }
++ }
++ },
++ "anon_name": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "display_name": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "message": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 5000
++ },
++ "deleted": {
++ "type": "boolean"
++ },
++ "score": {
++ "type": "integer"
++ },
++ "upvotes": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "downvotes": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "my_vote": {
++ "enum": [
++ 1,
++ -1,
++ 0
++ ]
++ },
++ "reply_count": {
++ "type": [
++ "integer",
++ "null"
++ ]
++ },
++ "created_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "edited_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "can_edit": {
++ "type": "boolean"
++ },
++ "can_delete": {
++ "type": "boolean"
++ },
++ "replies": {
++ "type": "array",
++ "items": {
++ "type": "object"
++ }
++ }
++ }
++ },
++ "community.forum-post.v1": {
++ "title": "CommunityForumPost",
++ "description": "community.forum-post@1: one forum post (the opening post of a thread or a reply) as OpenVibe.Community's API shows it (server/forum/service.js shapePost). A deleted post keeps its place with no author, body, attachments or pastes.",
++ "type": "object",
++ "required": [
++ "id",
++ "thread_id",
++ "is_opening",
++ "origin",
++ "author",
++ "body_markdown",
++ "body_html",
++ "revision",
++ "deleted",
++ "can_edit",
++ "attachments",
++ "pastes"
++ ],
++ "properties": {
++ "id": {
++ "type": "integer"
++ },
++ "thread_id": {
++ "type": "integer"
++ },
++ "is_opening": {
++ "type": "boolean"
++ },
++ "origin": {
++ "type": "string"
++ },
++ "author": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
++ "properties": {
++ "subject": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "username": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "display_name": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "avatar_url": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "profile_color": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "is_ai": {
++ "const": true
++ },
++ "is_system": {
++ "const": true
++ }
++ }
++ },
++ "body_markdown": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "body_html": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "revision": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "deleted": {
++ "type": "boolean"
++ },
++ "created_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "can_edit": {
++ "type": "boolean"
++ },
++ "attachments": {
++ "type": "array",
++ "maxItems": 4,
++ "items": {
++ "type": "object",
++ "required": [
++ "media_id",
++ "url"
++ ],
++ "properties": {
++ "media_id": {
++ "type": "string"
++ },
++ "url": {
++ "type": "string"
++ },
++ "filename": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "mime": {
++ "type": "string"
++ },
++ "size_bytes": {
++ "type": "integer"
++ }
++ }
++ }
++ },
++ "pastes": {
++ "type": "array",
++ "maxItems": 4,
++ "items": {
++ "type": "object",
++ "required": [
++ "slug",
++ "url"
++ ],
++ "properties": {
++ "slug": {
++ "type": "string"
++ },
++ "title": {
++ "type": "string"
++ },
++ "language": {
++ "type": "string"
++ },
++ "type": {
++ "type": "string"
++ },
++ "url": {
++ "type": "string"
++ },
++ "screenshot_url": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "lines": {
++ "type": "integer"
++ },
++ "excerpt": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++ }
}
}
},
-- "common.no-body.v1": {
-- "title": "NoBody",
-- "description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",
-- "type": "object",
-- "maxProperties": 0
-- },
-- "community.comment-thread.v1": {
-- "title": "CommunityCommentThread",
-- "description": "community.comment-thread@1: one embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeThread): the entity it belongs to, its visibility and comment count. id is the sequential id for services and the access id (cth_…) for everyone else; a hidden thread shows moderators its count, others only that it is hidden.",
++ "community.forum-space.v1": {
++ "title": "CommunityForumSpace",
++ "description": "community.forum-space@1: one forum space as OpenVibe.Community's /api/v1/spaces API shows it (server/forum/service.js shapeSpace): its slug, name, visibility, thread kind and statuses, style, whether votes and reactions are on, its group and parent board, the members-only gate and, in lists, its counts and last activity.",
"type": "object",
"required": [
-- "id",
-- "access_id",
-- "ref",
++ "slug",
++ "name",
"visibility",
-- "comment_count"
++ "url",
++ "thread_kind",
++ "statuses",
++ "style",
++ "votes",
++ "reactions",
++ "group",
++ "parent",
++ "members_only"
],
"properties": {
-- "id": {
-- "type": [
-- "integer",
-- "string"
-- ]
++ "slug": {
++ "type": "string"
},
-- "access_id": {
-- "type": "string",
-- "pattern": "^cth_"
++ "name": {
++ "type": "string"
},
-- "ref": {
-- "$ref": "#/components/schemas/common.entity-ref.v1"
++ "description": {
++ "type": [
++ "string",
++ "null"
++ ]
},
"visibility": {
"enum": [
"public",
-- "hidden",
-- "locked"
++ "members",
++ "staff"
]
},
-- "comment_count": {
-- "type": [
-- "integer",
-- "null"
++ "url": {
++ "type": "string"
++ },
++ "thread_kind": {
++ "type": "string"
++ },
++ "statuses": {
++ "type": "array",
++ "items": {
++ "type": "string"
++ }
++ },
++ "style": {
++ "enum": [
++ "feed",
++ "forum"
]
},
-- "created_at": {
-- "type": [
-- "string",
-- "null"
-- ],
-- "format": "date-time"
++ "votes": {
++ "type": "boolean"
},
-- "updated_at": {
++ "reactions": {
++ "type": "boolean"
++ },
++ "group": {
"type": [
-- "string",
++ "object",
"null"
],
-- "format": "date-time"
-- }
-- }
-- },
-- "community.comment-write-request.v1": {
-- "title": "CommunityCommentWriteRequest",
-- "description": "community.comment-write-request@1: bodies of community.comment.write on OpenVibe.Community (people, anonymous people with a name where the thread allows it, and services acting as X-OV-Subject or as AI). POST /api/v1/comments/threads/resolve: { ref } — get or create the thread of an entity (browsers only for the allow-listed entity types; a label is taken only from a service). POST /api/v1/comments/threads/:id/comments: { message, parent_id?, anon_name? } (message trimmed, at most 5000 characters; a reply to a reply joins the top-level comment). POST /api/v1/comments/:commentId/votes: { value: 1 | -1 | 0 } (people). GET /api/v1/comments/threads/:id and DELETE /api/v1/comments/:commentId take no body. Unknown fields are ignored.",
-- "anyOf": [
-- {
-- "type": "object",
-- "required": [
-- "ref"
-- ],
"properties": {
-- "ref": {
-- "$ref": "#/components/schemas/common.entity-ref.v1"
++ "slug": {
++ "type": "string"
++ },
++ "name": {
++ "type": "string"
}
}
},
-- {
-- "type": "object",
-- "required": [
-- "message"
++ "parent": {
++ "type": [
++ "object",
++ "null"
],
"properties": {
-- "message": {
-- "type": "string",
-- "minLength": 1,
-- "maxLength": 5000,
-- "pattern": "\\S"
-- },
-- "parent_id": {
-- "type": [
-- "integer",
-- "string",
-- "null"
-- ]
++ "slug": {
++ "type": "string"
},
-- "anon_name": {
-- "type": [
-- "string",
-- "null"
-- ]
++ "name": {
++ "type": "string"
}
}
},
-- {
-- "type": "object",
-- "required": [
-- "value"
-- ],
-- "properties": {
-- "value": {
-- "enum": [
-- 1,
-- -1,
-- 0,
-- "1",
-- "-1",
-- "0"
-- ]
-- }
-- }
++ "post_count": {
++ "type": "integer"
},
-- {
-- "$ref": "#/components/schemas/common.no-body.v1"
-- }
-- ]
-- },
-- "community.comment-write-result.v1": {
-- "title": "CommunityCommentWriteResult",
-- "description": "community.comment-write-result@1: answers of community.comment.write on OpenVibe.Community. POST …/threads/resolve → { thread, created } (201 when created); GET …/threads/:id?after=&sort=&limit=&parent= → { thread, comments, next_cursor, viewer }; POST …/threads/:id/comments → 201 { comment }; DELETE /api/v1/comments/:commentId → { ok, id }; POST …/:commentId/votes → { comment_id, score, upvotes, downvotes, my_vote }. Refusals are problem+json (thread.locked, auth.required, community.blocked, …).",
-- "anyOf": [
-- {
-- "type": "object",
-- "required": [
-- "thread",
-- "created"
++ "thread_count": {
++ "type": "integer"
++ },
++ "last_activity_at": {
++ "type": [
++ "string",
++ "null"
],
-- "properties": {
-- "thread": {
-- "$ref": "#/components/schemas/community.comment-thread.v1"
-- },
-- "created": {
-- "type": "boolean"
-- }
-- }
++ "format": "date-time"
},
-- {
-- "type": "object",
-- "required": [
-- "thread",
-- "comments",
-- "next_cursor",
-- "viewer"
++ "members_only": {
++ "type": [
++ "object",
++ "null"
],
"properties": {
-- "thread": {
-- "$ref": "#/components/schemas/community.comment-thread.v1"
-- },
-- "comments": {
-- "type": "array",
-- "items": {
-- "$ref": "#/components/schemas/community.comment.v1"
-- }
++ "owner": {
++ "type": "string"
},
-- "next_cursor": {
++ "owner_username": {
"type": [
"string",
"null"
]
},
-- "viewer": {
-- "type": "object",
-- "properties": {
-- "signed_in": {
-- "type": "boolean"
-- },
-- "can_comment": {
-- "type": "boolean"
-- },
-- "can_vote": {
-- "type": "boolean"
-- },
-- "can_moderate": {
-- "type": "boolean"
-- }
-- }
-- }
-- }
-- },
-- {
-- "type": "object",
-- "required": [
-- "comment"
-- ],
-- "properties": {
-- "comment": {
-- "$ref": "#/components/schemas/community.comment.v1"
-- }
-- }
-- },
-- {
-- "type": "object",
-- "required": [
-- "ok",
-- "id"
-- ],
-- "properties": {
-- "ok": {
-- "const": true
-- },
-- "id": {
-- "type": "integer"
-- }
-- }
-- },
-- {
-- "type": "object",
-- "required": [
-- "comment_id",
-- "score",
-- "upvotes",
-- "downvotes",
-- "my_vote"
-- ],
-- "properties": {
-- "comment_id": {
-- "type": "integer"
-- },
-- "score": {
-- "type": "integer"
-- },
-- "upvotes": {
-- "type": "integer",
-- "minimum": 0
-- },
-- "downvotes": {
-- "type": "integer",
-- "minimum": 0
-- },
-- "my_vote": {
-- "enum": [
-- 1,
-- -1,
-- 0
++ "join_url": {
++ "type": [
++ "string",
++ "null"
]
}
}
++ },
++ "last_post": {
++ "type": [
++ "object",
++ "null"
++ ]
++ },
++ "children": {
++ "type": "array"
}
-- ]
++ }
},
-- "community.comment.v1": {
-- "title": "CommunityComment",
-- "description": "community.comment@1: one comment in an embeddable comment thread on OpenVibe.Community (server/comments/service.js shapeComment), with its author (or anonymous name), score and the caller's vote, reply count and, for top-level comments in a page, the first replies. A deleted comment keeps its place with no author or message.",
++ "community.forum-thread.v1": {
++ "title": "CommunityForumThread",
++ "description": "community.forum-thread@1: one forum thread as OpenVibe.Community's /api/v1/spaces API shows it (server/forum/service.js shapeThread): where it lives, its kind and status, category, members-only gate, author, origin, pin and lock state, score, reply count, views, pages, the last post and the caller's vote.",
"type": "object",
"required": [
"id",
-- "thread_id",
-- "parent_id",
-- "origin",
++ "space",
++ "slug",
++ "title",
++ "url",
++ "kind",
"author",
-- "message",
-- "deleted",
++ "origin",
++ "pinned",
++ "locked",
"score",
-- "my_vote",
-- "can_edit",
-- "can_delete"
++ "reply_count",
++ "pages",
++ "created_at",
++ "my_vote"
],
"properties": {
"id": {
"type": "integer"
},
-- "thread_id": {
++ "space": {
++ "type": "string"
++ },
++ "slug": {
++ "type": "string"
++ },
++ "title": {
++ "type": "string",
++ "maxLength": 200
++ },
++ "url": {
++ "type": "string",
++ "description": "/s/<space>/t/<slug>"
++ },
++ "kind": {
++ "type": "string",
++ "description": "discussion, request, roadmap, …"
++ },
++ "status": {
"type": [
-- "integer",
-- "string"
++ "string",
++ "null"
]
},
-- "parent_id": {
++ "category": {
"type": [
-- "integer",
++ "object",
"null"
-- ]
++ ],
++ "properties": {
++ "slug": {
++ "type": "string"
++ },
++ "name": {
++ "type": "string"
++ },
++ "description": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "position": {},
++ "thread_count": {
++ "type": "integer"
++ }
++ }
},
-- "origin": {
-- "type": "string"
++ "members_only": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "properties": {
++ "owner": {
++ "type": "string"
++ },
++ "owner_username": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "join_url": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
},
"author": {
"type": [
"object",
"null"
],
++ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
"properties": {
"subject": {
"type": [
@@@ -1861,84 +3223,107 @@
}
}
},
-- "anon_name": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "display_name": {
-- "type": [
-- "string",
-- "null"
-- ]
++ "origin": {
++ "type": "string"
},
-- "message": {
-- "type": [
-- "string",
-- "null"
-- ],
-- "maxLength": 5000
++ "pinned": {
++ "type": "boolean"
},
-- "deleted": {
++ "locked": {
"type": "boolean"
},
"score": {
"type": "integer"
},
-- "upvotes": {
++ "reply_count": {
"type": "integer",
"minimum": 0
},
-- "downvotes": {
++ "views": {
"type": "integer",
"minimum": 0
},
-- "my_vote": {
-- "enum": [
-- 1,
-- -1,
-- 0
-- ]
-- },
-- "reply_count": {
-- "type": [
-- "integer",
-- "null"
-- ]
++ "pages": {
++ "type": "integer",
++ "minimum": 1
},
-- "created_at": {
++ "last_post": {
"type": [
-- "string",
++ "object",
"null"
],
-- "format": "date-time"
++ "properties": {
++ "id": {
++ "type": "integer"
++ },
++ "author": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
++ "properties": {
++ "subject": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "username": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "display_name": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "avatar_url": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "profile_color": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "is_ai": {
++ "const": true
++ },
++ "is_system": {
++ "const": true
++ }
++ }
++ }
++ }
},
-- "updated_at": {
++ "crosspost_of": {},
++ "last_activity_at": {
"type": [
"string",
"null"
],
"format": "date-time"
},
-- "edited_at": {
++ "created_at": {
"type": [
"string",
"null"
],
"format": "date-time"
},
-- "can_edit": {
-- "type": "boolean"
-- },
-- "can_delete": {
-- "type": "boolean"
-- },
-- "replies": {
-- "type": "array",
-- "items": {
-- "type": "object"
-- }
++ "my_vote": {
++ "enum": [
++ 1,
++ -1,
++ 0
++ ]
}
}
},
@@@ -2898,36 +4283,245 @@
"null"
]
},
-- "is_owner": {
-- "type": "boolean",
-- "description": "Whether the caller owns it."
++ "is_owner": {
++ "type": "boolean",
++ "description": "Whether the caller owns it."
++ },
++ "username": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "display_name": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "OpenVibe AI for an AI paste."
++ },
++ "avatar_url": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "profile_color": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++ },
++ "community.post-write-request.v1": {
++ "title": "CommunityPostWriteRequest",
++ "description": "community.post-write-request@1: bodies of the forum writes on OpenVibe.Community (community.post.create; a service writes as X-OV-Subject, or as AI with X-OV-Origin: ai). POST /api/v1/spaces/:space/threads: { title, body, members_only?, category?, attachments?, pastes? } (title 3 to 200 characters after whitespace is collapsed; body Markdown, non-blank, at most 40000 characters; body_markdown is accepted for body). POST …/threads/:slug/posts (a reply) and PUT /api/v1/posts/:id (an edit): { body, attachments?, pastes? }. POST …/threads/:slug/votes: { value: 1 | -1 | 0 }. DELETE /api/v1/posts/:id and DELETE /api/v1/spaces/:space/threads/:slug take no body. Unknown fields are ignored; refusals are problem+json (post.empty, post.too_long, thread.invalid_title, vote.invalid, thread.locked, vip.members_only, …).",
++ "anyOf": [
++ {
++ "type": "object",
++ "anyOf": [
++ {
++ "required": [
++ "body"
++ ]
++ },
++ {
++ "required": [
++ "body_markdown"
++ ]
++ }
++ ],
++ "properties": {
++ "title": {
++ "type": "string",
++ "description": "A new thread's title."
++ },
++ "body": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 40000,
++ "description": "Markdown."
++ },
++ "body_markdown": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 40000,
++ "description": "Read when body is absent."
++ },
++ "members_only": {
++ "description": "A new thread only: true gates it to the author's own VIP members; { owner: usr_… } (or the subject itself) names the creator (moderators, or the author naming themselves); null or false: open.",
++ "anyOf": [
++ {
++ "type": [
++ "boolean",
++ "null"
++ ]
++ },
++ {
++ "type": "string",
++ "pattern": "^usr_"
++ },
++ {
++ "type": "object",
++ "required": [
++ "owner"
++ ],
++ "properties": {
++ "owner": {
++ "type": "string",
++ "pattern": "^usr_"
++ }
++ }
++ }
++ ]
++ },
++ "category": {
++ "type": "string",
++ "description": "A new thread only: the slug of one of the space's categories."
++ },
++ "attachments": {
++ "description": "At most 4 images the writer uploaded (POST /api/v1/spaces/:space/attachments) and has not used yet, by media id. People only.",
++ "anyOf": [
++ {
++ "type": "string",
++ "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ {
++ "type": "array",
++ "maxItems": 4,
++ "items": {
++ "type": "string",
++ "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ }
++ ]
++ },
++ "pastes": {
++ "description": "At most 4 shareable pastes (not private, not burn-after-read) by slug or /p/<slug> link: an array, or one string separated by spaces or commas.",
++ "type": [
++ "array",
++ "string"
++ ],
++ "items": {
++ "type": "string"
++ }
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "value"
++ ],
++ "properties": {
++ "value": {
++ "enum": [
++ 1,
++ -1,
++ 0,
++ "1",
++ "-1",
++ "0"
++ ],
++ "description": "A thread vote; 0 takes the vote back."
++ }
++ }
},
-- "username": {
-- "type": [
-- "string",
-- "null"
-- ]
++ {
++ "$ref": "#/components/schemas/common.no-body.v1"
++ }
++ ]
++ },
++ "community.post-write-result.v1": {
++ "title": "CommunityPostWriteResult",
++ "description": "community.post-write-result@1: answers of the forum writes on OpenVibe.Community (community.post.create). POST /api/v1/spaces/:space/threads → 201 { thread, post } (the thread and its opening post); POST …/threads/:slug/posts → 201 { post, page, url } (the reply, the page it lands on and its address with #post-<id>); PUT /api/v1/posts/:id → { post }; POST …/threads/:slug/votes → { thread_id, score, upvotes, downvotes, my_vote }; DELETE /api/v1/posts/:id → { ok, id } (deleting the opening post deletes the thread: { ok, id, deleted: thread }); DELETE /api/v1/spaces/:space/threads/:slug → { ok, id, deleted: thread }. Refusals are problem+json.",
++ "anyOf": [
++ {
++ "type": "object",
++ "required": [
++ "thread",
++ "post"
++ ],
++ "properties": {
++ "thread": {
++ "$ref": "#/components/schemas/community.forum-thread.v1"
++ },
++ "post": {
++ "$ref": "#/components/schemas/community.forum-post.v1"
++ }
++ }
},
-- "display_name": {
-- "type": [
-- "string",
-- "null"
++ {
++ "type": "object",
++ "required": [
++ "post"
],
-- "description": "OpenVibe AI for an AI paste."
++ "properties": {
++ "post": {
++ "$ref": "#/components/schemas/community.forum-post.v1"
++ },
++ "page": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "url": {
++ "type": "string"
++ }
++ }
},
-- "avatar_url": {
-- "type": [
-- "string",
-- "null"
-- ]
++ {
++ "type": "object",
++ "required": [
++ "thread_id",
++ "score",
++ "upvotes",
++ "downvotes",
++ "my_vote"
++ ],
++ "properties": {
++ "thread_id": {
++ "type": "integer"
++ },
++ "score": {
++ "type": "integer"
++ },
++ "upvotes": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "downvotes": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "my_vote": {
++ "enum": [
++ 1,
++ -1,
++ 0
++ ]
++ }
++ }
},
-- "profile_color": {
-- "type": [
-- "string",
-- "null"
-- ]
++ {
++ "type": "object",
++ "required": [
++ "ok",
++ "id"
++ ],
++ "properties": {
++ "ok": {
++ "const": true
++ },
++ "id": {
++ "type": "integer"
++ },
++ "deleted": {
++ "const": "thread"
++ }
++ }
}
-- }
++ ]
},
"community.pulse-item.v1": {
"title": "CommunityPulseItem",
@@@ -3161,6 +4755,206 @@
}
]
},
++ "community.space-read-result.v1": {
++ "title": "CommunitySpaceReadResult",
++ "description": "community.space-read-result@1: answers of community.space.read on OpenVibe.Community. GET /api/v1/spaces → { spaces, groups } (the spaces the caller can open, each with its last post, and the board index: groups in order with their top-level spaces and child boards); GET /api/v1/spaces/:space → { space } (a staff or members-only space the caller cannot open is 404 or 401/403 vip.members_only); GET /s is the board index as an HTML page.",
++ "anyOf": [
++ {
++ "type": "object",
++ "required": [
++ "spaces",
++ "groups"
++ ],
++ "properties": {
++ "spaces": {
++ "type": "array",
++ "items": {
++ "$ref": "#/components/schemas/community.forum-space.v1"
++ }
++ },
++ "groups": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "required": [
++ "slug",
++ "name",
++ "spaces"
++ ],
++ "properties": {
++ "slug": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "name": {
++ "type": "string"
++ },
++ "description": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "spaces": {
++ "type": "array"
++ }
++ }
++ }
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "space"
++ ],
++ "properties": {
++ "space": {
++ "$ref": "#/components/schemas/community.forum-space.v1"
++ }
++ }
++ },
++ {
++ "type": "string",
++ "contentMediaType": "text/html",
++ "description": "GET /s: the board index page."
++ }
++ ]
++ },
++ "community.thread-read-result.v1": {
++ "title": "CommunityThreadReadResult",
++ "description": "community.thread-read-result@1: answers of community.thread.read on OpenVibe.Community. GET /api/v1/spaces/:space/threads?sort=&page=&limit=&category=&status= → a page of threads with the space, paging, categories, children and what the viewer may do; GET /api/v1/spaces/:space/threads/:slug?page= → the thread with a page of posts (reactions, author stats, crosspost info, attachments settings, paging, viewer). GET /s/:space and /s/:space/t/:slug are the same as HTML pages; GET /s/feed.xml and /s/:space/feed.xml are RSS feeds of public threads.",
++ "anyOf": [
++ {
++ "type": "object",
++ "required": [
++ "space",
++ "threads",
++ "page",
++ "pages",
++ "total"
++ ],
++ "properties": {
++ "space": {
++ "type": "object"
++ },
++ "sort": {
++ "type": "string"
++ },
++ "page": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "per_page": {
++ "type": "integer"
++ },
++ "total": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "pages": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "categories": {
++ "type": "array"
++ },
++ "category": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "status": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "viewer": {
++ "type": "object"
++ },
++ "children": {
++ "type": "array"
++ },
++ "groups": {
++ "type": "array"
++ },
++ "threads": {
++ "type": "array",
++ "items": {
++ "$ref": "#/components/schemas/community.forum-thread.v1"
++ }
++ }
++ }
++ },
++ {
++ "type": "object",
++ "required": [
++ "space",
++ "thread",
++ "posts",
++ "page",
++ "pages"
++ ],
++ "properties": {
++ "space": {
++ "type": "object"
++ },
++ "thread": {
++ "$ref": "#/components/schemas/community.forum-thread.v1"
++ },
++ "posts": {
++ "type": "array",
++ "items": {
++ "$ref": "#/components/schemas/community.forum-post.v1"
++ }
++ },
++ "reactions": {
++ "type": "array"
++ },
++ "crosspost": {
++ "type": "object"
++ },
++ "categories": {
++ "type": "array"
++ },
++ "attachments": {
++ "type": "object"
++ },
++ "page": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "per_page": {
++ "type": "integer"
++ },
++ "pages": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "total": {
++ "type": "integer"
++ },
++ "viewer": {
++ "type": "object"
++ }
++ }
++ },
++ {
++ "type": "string",
++ "contentMediaType": "text/html",
++ "description": "GET /s/:space and /s/:space/t/:slug: HTML pages."
++ },
++ {
++ "type": "string",
++ "contentMediaType": "application/rss+xml",
++ "description": "GET /s/feed.xml and /s/:space/feed.xml: RSS feeds."
++ }
++ ]
++ },
"errors.problem.v1": {
"title": "Problem",
"description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
ddiff --git a/generated/openapi/coupons.json b/generated/openapi/coupons.json
iindex b8a45314..b65871ef 100644
---- a/generated/openapi/coupons.json
++++ b/generated/openapi/coupons.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Coupons API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/deals.json b/generated/openapi/deals.json
iindex 41193e14..791dd0a8 100644
---- a/generated/openapi/deals.json
++++ b/generated/openapi/deals.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Deals API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/events.json b/generated/openapi/events.json
iindex 3df675a7..6faf0119 100644
---- a/generated/openapi/events.json
++++ b/generated/openapi/events.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Events API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/food.json b/generated/openapi/food.json
iindex 2b4faf36..db1f241d 100644
---- a/generated/openapi/food.json
++++ b/generated/openapi/food.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Food API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/games.json b/generated/openapi/games.json
iindex c96a1dcc..c4757ab7 100644
---- a/generated/openapi/games.json
++++ b/generated/openapi/games.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Games API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/host.json b/generated/openapi/host.json
iindex f4e49f47..e9f66552 100644
---- a/generated/openapi/host.json
++++ b/generated/openapi/host.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Host API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/index.json b/generated/openapi/index.json
iindex b14118b8..6f793622 100644
---- a/generated/openapi/index.json
++++ b/generated/openapi/index.json
@@@ -47,8 +47,8 @@
"service": "community",
"name": "OpenVibe.Community",
"origin": "https://openvibe.community",
-- "operations": 24,
-- "capabilities": 7
++ "operations": 37,
++ "capabilities": 11
},
{
"service": "coupons",
@@@ -162,13 +162,6 @@
"operations": 13,
"capabilities": 3
},
-- {
-- "service": "space",
-- "name": "OpenVibe.Space",
-- "origin": "https://openvibe.space",
-- "operations": 30,
-- "capabilities": 4
-- },
{
"service": "tips",
"name": "OpenVibe.Tips",
ddiff --git a/generated/openapi/live.json b/generated/openapi/live.json
iindex 858346f5..8a669285 100644
---- a/generated/openapi/live.json
++++ b/generated/openapi/live.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Live API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/media.json b/generated/openapi/media.json
iindex 6d2bd44c..043ac0ba 100644
---- a/generated/openapi/media.json
++++ b/generated/openapi/media.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Media API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/network.json b/generated/openapi/network.json
iindex 5a8f7463..42222b99 100644
---- a/generated/openapi/network.json
++++ b/generated/openapi/network.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Network API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/news.json b/generated/openapi/news.json
iindex f60fdef6..87afc165 100644
---- a/generated/openapi/news.json
++++ b/generated/openapi/news.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.News API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/openre.json b/generated/openapi/openre.json
iindex 0ca225a6..325eb05c 100644
---- a/generated/openapi/openre.json
++++ b/generated/openapi/openre.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenRe.Stream API",
-- "version": "0.117.0",
-- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/reviews.json b/generated/openapi/reviews.json
iindex 235004a1..c2357809 100644
---- a/generated/openapi/reviews.json
++++ b/generated/openapi/reviews.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Reviews API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/run.json b/generated/openapi/run.json
iindex fdb08ee7..73eb396a 100644
---- a/generated/openapi/run.json
++++ b/generated/openapi/run.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Run API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/search.json b/generated/openapi/search.json
iindex bc7db1a0..6be9fc26 100644
---- a/generated/openapi/search.json
++++ b/generated/openapi/search.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Search API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/services.json b/generated/openapi/services.json
iindex b93a56c1..960b04b7 100644
---- a/generated/openapi/services.json
++++ b/generated/openapi/services.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Services API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/sources.json b/generated/openapi/sources.json
iindex 5602b3e4..ea27c44b 100644
---- a/generated/openapi/sources.json
++++ b/generated/openapi/sources.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Sources API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/space.json b/generated/openapi/space.json
ddeleted file mode 100644
iindex 058e69d4..00000000
---- a/generated/openapi/space.json
++++ /dev/null
@@@ -1,2802 +0,0 @@
--{
-- "openapi": "3.1.0",
-- "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
-- "info": {
-- "title": "OpenVibe.Space API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
-- "license": {
-- "name": "MIT",
-- "identifier": "MIT"
-- }
-- },
-- "servers": [
-- {
-- "url": "https://openvibe.space"
-- }
-- ],
-- "paths": {
-- "/api/v1/posts/{id}": {
-- "delete": {
-- "operationId": "delete_api_v1_posts_id",
-- "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",
-- "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
-- "tags": [
-- "space.post"
-- ],
-- "x-openvibe-capabilities": [
-- "space.post.write"
-- ],
-- "x-openvibe-visibility": [
-- "public"
-- ],
-- "x-openvibe-quota-class": "space-write",
-- "security": [
-- {
-- "openvibeToken": [
-- "space.post.write"
-- ]
-- }
-- ],
-- "x-openvibe-input": {
-- "$ref": "#/components/schemas/space.post-write-request.v1"
-- },
-- "parameters": [
-- {
-- "name": "id",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- }
-- ],
-- "responses": {
-- "2XX": {
-- "description": "Success",
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.post-write-result.v1"
-- }
-- }
-- }
-- },
-- "default": {
-- "description": "An error (RFC 9457 problem details)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- },
-- "put": {
-- "operationId": "put_api_v1_posts_id",
-- "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",
-- "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
-- "tags": [
-- "space.post"
-- ],
-- "x-openvibe-capabilities": [
-- "space.post.write"
-- ],
-- "x-openvibe-visibility": [
-- "public"
-- ],
-- "x-openvibe-quota-class": "space-write",
-- "security": [
-- {
-- "openvibeToken": [
-- "space.post.write"
-- ]
-- }
-- ],
-- "requestBody": {
-- "required": false,
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.post-write-request.v1"
-- }
-- }
-- }
-- },
-- "parameters": [
-- {
-- "name": "id",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- }
-- ],
-- "responses": {
-- "2XX": {
-- "description": "Success",
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.post-write-result.v1"
-- }
-- }
-- }
-- },
-- "default": {
-- "description": "An error (RFC 9457 problem details)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- }
-- },
-- "/api/v1/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",
-- "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": [
-- "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}/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.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"
-- }
-- }
-- }
-- }
-- }
-- },
-- "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)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- }
-- },
-- "/api/v1/spaces/{space}/threads": {
-- "get": {
-- "operationId": "get_api_v1_spaces_space_threads",
-- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",
-- "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write.",
-- "tags": [
-- "space.thread"
-- ],
-- "x-openvibe-capabilities": [
-- "space.thread.read"
-- ],
-- "x-openvibe-visibility": [
-- "public"
-- ],
-- "x-openvibe-quota-class": "space-read",
-- "security": [
-- {
-- "openvibeToken": [
-- "space.thread.read"
-- ]
-- }
-- ],
-- "parameters": [
-- {
-- "name": "space",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- }
-- ],
-- "responses": {
-- "2XX": {
-- "description": "Success",
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.thread-read-result.v1"
-- }
-- }
-- }
-- },
-- "default": {
-- "description": "An error (RFC 9457 problem details)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- },
-- "post": {
-- "operationId": "post_api_v1_spaces_space_threads",
-- "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",
-- "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
-- "tags": [
-- "space.post"
-- ],
-- "x-openvibe-capabilities": [
-- "space.post.write"
-- ],
-- "x-openvibe-visibility": [
-- "public"
-- ],
-- "x-openvibe-quota-class": "space-write",
-- "security": [
-- {
-- "openvibeToken": [
-- "space.post.write"
-- ]
-- }
-- ],
-- "requestBody": {
-- "required": false,
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.post-write-request.v1"
-- }
-- }
-- }
-- },
-- "parameters": [
-- {
-- "name": "space",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- }
-- ],
-- "responses": {
-- "2XX": {
-- "description": "Success",
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.post-write-result.v1"
-- }
-- }
-- }
-- },
-- "default": {
-- "description": "An error (RFC 9457 problem details)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- }
-- },
-- "/api/v1/spaces/{space}/threads/{slug}": {
-- "delete": {
-- "operationId": "delete_api_v1_spaces_space_threads_slug",
-- "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",
-- "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
-- "tags": [
-- "space.post"
-- ],
-- "x-openvibe-capabilities": [
-- "space.post.write"
-- ],
-- "x-openvibe-visibility": [
-- "public"
-- ],
-- "x-openvibe-quota-class": "space-write",
-- "security": [
-- {
-- "openvibeToken": [
-- "space.post.write"
-- ]
-- }
-- ],
-- "x-openvibe-input": {
-- "$ref": "#/components/schemas/space.post-write-request.v1"
-- },
-- "parameters": [
-- {
-- "name": "space",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- },
-- {
-- "name": "slug",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- }
-- ],
-- "responses": {
-- "2XX": {
-- "description": "Success",
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.post-write-result.v1"
-- }
-- }
-- }
-- },
-- "default": {
-- "description": "An error (RFC 9457 problem details)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- },
-- "get": {
-- "operationId": "get_api_v1_spaces_space_threads_slug",
-- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",
-- "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write.",
-- "tags": [
-- "space.thread"
-- ],
-- "x-openvibe-capabilities": [
-- "space.thread.read"
-- ],
-- "x-openvibe-visibility": [
-- "public"
-- ],
-- "x-openvibe-quota-class": "space-read",
-- "security": [
-- {
-- "openvibeToken": [
-- "space.thread.read"
-- ]
-- }
-- ],
-- "parameters": [
-- {
-- "name": "space",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- },
-- {
-- "name": "slug",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- }
-- ],
-- "responses": {
-- "2XX": {
-- "description": "Success",
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.thread-read-result.v1"
-- }
-- }
-- }
-- },
-- "default": {
-- "description": "An error (RFC 9457 problem details)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- }
-- },
-- "/api/v1/spaces/{space}/threads/{slug}/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",
-- "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}/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",
-- "summary": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Sub",
-- "description": "**space.post.write** (public): Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
-- "tags": [
-- "space.post"
-- ],
-- "x-openvibe-capabilities": [
-- "space.post.write"
-- ],
-- "x-openvibe-visibility": [
-- "public"
-- ],
-- "x-openvibe-quota-class": "space-write",
-- "security": [
-- {
-- "openvibeToken": [
-- "space.post.write"
-- ]
-- }
-- ],
-- "requestBody": {
-- "required": false,
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.post-write-request.v1"
-- }
-- }
-- }
-- },
-- "parameters": [
-- {
-- "name": "space",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- },
-- {
-- "name": "slug",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- }
-- ],
-- "responses": {
-- "2XX": {
-- "description": "Success",
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.post-write-result.v1"
-- }
-- }
-- }
-- },
-- "default": {
-- "description": "An error (RFC 9457 problem details)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- }
-- },
-- "/s": {
-- "get": {
-- "operationId": "get_s",
-- "summary": "List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum).",
-- "description": "**space.forum.read** (public): List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Space checks space visibility itself.",
-- "tags": [
-- "space.forum"
-- ],
-- "x-openvibe-capabilities": [
-- "space.forum.read"
-- ],
-- "x-openvibe-visibility": [
-- "public"
-- ],
-- "x-openvibe-quota-class": "space-read",
-- "security": [
-- {
-- "openvibeToken": [
-- "space.forum.read"
-- ]
-- }
-- ],
-- "responses": {
-- "2XX": {
-- "description": "Success",
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.forum-read-result.v1"
-- }
-- }
-- }
-- },
-- "default": {
-- "description": "An error (RFC 9457 problem details)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- }
-- },
-- "/s/{space}": {
-- "get": {
-- "operationId": "get_s_space",
-- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",
-- "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write.",
-- "tags": [
-- "space.thread"
-- ],
-- "x-openvibe-capabilities": [
-- "space.thread.read"
-- ],
-- "x-openvibe-visibility": [
-- "public"
-- ],
-- "x-openvibe-quota-class": "space-read",
-- "security": [
-- {
-- "openvibeToken": [
-- "space.thread.read"
-- ]
-- }
-- ],
-- "parameters": [
-- {
-- "name": "space",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- }
-- ],
-- "responses": {
-- "2XX": {
-- "description": "Success",
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.thread-read-result.v1"
-- }
-- }
-- }
-- },
-- "default": {
-- "description": "An error (RFC 9457 problem details)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- }
-- },
-- "/s/{space}/feed.xml": {
-- "get": {
-- "operationId": "get_s_space_feed_xml",
-- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",
-- "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write.",
-- "tags": [
-- "space.thread"
-- ],
-- "x-openvibe-capabilities": [
-- "space.thread.read"
-- ],
-- "x-openvibe-visibility": [
-- "public"
-- ],
-- "x-openvibe-quota-class": "space-read",
-- "security": [
-- {
-- "openvibeToken": [
-- "space.thread.read"
-- ]
-- }
-- ],
-- "parameters": [
-- {
-- "name": "space",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- }
-- ],
-- "responses": {
-- "2XX": {
-- "description": "Success",
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.thread-read-result.v1"
-- }
-- }
-- }
-- },
-- "default": {
-- "description": "An error (RFC 9457 problem details)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- }
-- },
-- "/s/{space}/t/{slug}": {
-- "get": {
-- "operationId": "get_s_space_t_slug",
-- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",
-- "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write.",
-- "tags": [
-- "space.thread"
-- ],
-- "x-openvibe-capabilities": [
-- "space.thread.read"
-- ],
-- "x-openvibe-visibility": [
-- "public"
-- ],
-- "x-openvibe-quota-class": "space-read",
-- "security": [
-- {
-- "openvibeToken": [
-- "space.thread.read"
-- ]
-- }
-- ],
-- "parameters": [
-- {
-- "name": "space",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- },
-- {
-- "name": "slug",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- }
-- ],
-- "responses": {
-- "2XX": {
-- "description": "Success",
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.thread-read-result.v1"
-- }
-- }
-- }
-- },
-- "default": {
-- "description": "An error (RFC 9457 problem details)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- }
-- },
-- "/s/feed.xml": {
-- "get": {
-- "operationId": "get_s_feed_xml",
-- "summary": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3).",
-- "description": "**space.thread.read** (public): Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds (renamed from community.thread.read, plan T10 D3). Space checks space visibility and the VIP gate (403 vip.members_only); writing threads and replies is space.post.write.",
-- "tags": [
-- "space.thread"
-- ],
-- "x-openvibe-capabilities": [
-- "space.thread.read"
-- ],
-- "x-openvibe-visibility": [
-- "public"
-- ],
-- "x-openvibe-quota-class": "space-read",
-- "security": [
-- {
-- "openvibeToken": [
-- "space.thread.read"
-- ]
-- }
-- ],
-- "responses": {
-- "2XX": {
-- "description": "Success",
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/space.thread-read-result.v1"
-- }
-- }
-- }
-- },
-- "default": {
-- "description": "An error (RFC 9457 problem details)",
-- "content": {
-- "application/problem+json": {
-- "schema": {
-- "$ref": "#/components/schemas/errors.problem.v1"
-- }
-- }
-- }
-- }
-- }
-- }
-- }
-- },
-- "components": {
-- "schemas": {
-- "common.no-body.v1": {
-- "title": "NoBody",
-- "description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",
-- "type": "object",
-- "maxProperties": 0
-- },
-- "errors.problem.v1": {
-- "title": "Problem",
-- "description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
-- "type": "object",
-- "required": [
-- "type",
-- "title",
-- "status",
-- "code"
-- ],
-- "additionalProperties": true,
-- "properties": {
-- "type": {
-- "type": "string",
-- "format": "uri-reference"
-- },
-- "title": {
-- "type": "string",
-- "minLength": 1
-- },
-- "status": {
-- "type": "integer",
-- "minimum": 400,
-- "maximum": 599
-- },
-- "detail": {
-- "type": "string"
-- },
-- "instance": {
-- "type": "string",
-- "format": "uri-reference"
-- },
-- "code": {
-- "type": "string",
-- "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$"
-- },
-- "request_id": {
-- "type": "string"
-- },
-- "trace_id": {
-- "type": "string",
-- "pattern": "^[0-9a-f]{32}$"
-- },
-- "errors": {
-- "type": "array",
-- "items": {
-- "type": "object",
-- "required": [
-- "message"
-- ],
-- "properties": {
-- "path": {
-- "type": "string"
-- },
-- "message": {
-- "type": "string"
-- }
-- }
-- }
-- },
-- "error": {
-- "type": "string",
-- "description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."
-- }
-- }
-- },
-- "space.forum-post.v1": {
-- "title": "SpaceForumPost",
-- "description": "space.forum-post@1: one forum post (the opening post of a thread or a reply) as OpenVibe.Space's API shows it (server/forum/service.js shapePost). A deleted post keeps its place with no author, body, attachments or pastes.",
-- "type": "object",
-- "required": [
-- "id",
-- "thread_id",
-- "is_opening",
-- "origin",
-- "author",
-- "body_markdown",
-- "body_html",
-- "revision",
-- "deleted",
-- "can_edit",
-- "attachments",
-- "pastes"
-- ],
-- "properties": {
-- "id": {
-- "type": "integer"
-- },
-- "thread_id": {
-- "type": "integer"
-- },
-- "is_opening": {
-- "type": "boolean"
-- },
-- "origin": {
-- "type": "string"
-- },
-- "author": {
-- "type": [
-- "object",
-- "null"
-- ],
-- "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
-- "properties": {
-- "subject": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "username": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "display_name": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "avatar_url": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "profile_color": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "is_ai": {
-- "const": true
-- },
-- "is_system": {
-- "const": true
-- }
-- }
-- },
-- "body_markdown": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "body_html": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "revision": {
-- "type": "integer",
-- "minimum": 1
-- },
-- "deleted": {
-- "type": "boolean"
-- },
-- "created_at": {
-- "type": [
-- "string",
-- "null"
-- ],
-- "format": "date-time"
-- },
-- "updated_at": {
-- "type": [
-- "string",
-- "null"
-- ],
-- "format": "date-time"
-- },
-- "can_edit": {
-- "type": "boolean"
-- },
-- "attachments": {
-- "type": "array",
-- "maxItems": 4,
-- "items": {
-- "type": "object",
-- "required": [
-- "media_id",
-- "url"
-- ],
-- "properties": {
-- "media_id": {
-- "type": "string"
-- },
-- "url": {
-- "type": "string"
-- },
-- "filename": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "mime": {
-- "type": "string"
-- },
-- "size_bytes": {
-- "type": "integer"
-- }
-- }
-- }
-- },
-- "pastes": {
-- "type": "array",
-- "maxItems": 4,
-- "items": {
-- "type": "object",
-- "required": [
-- "slug",
-- "url"
-- ],
-- "properties": {
-- "slug": {
-- "type": "string"
-- },
-- "title": {
-- "type": "string"
-- },
-- "language": {
-- "type": "string"
-- },
-- "type": {
-- "type": "string"
-- },
-- "url": {
-- "type": "string"
-- },
-- "screenshot_url": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "lines": {
-- "type": "integer"
-- },
-- "excerpt": {
-- "type": [
-- "string",
-- "null"
-- ]
-- }
-- }
-- }
-- }
-- }
-- },
-- "space.forum-read-result.v1": {
-- "title": "SpaceForumReadResult",
-- "description": "space.forum-read-result@1: answers of space.forum.read on OpenVibe.Space. GET /api/v1/spaces → { spaces, groups } (the spaces the caller can open, each with its last post, and the board index: groups in order with their top-level spaces and child boards); GET /api/v1/spaces/:space → { space } (a staff or members-only space the caller cannot open is 404 or 401/403 vip.members_only); GET /s is the board index as an HTML page.",
-- "anyOf": [
-- {
-- "type": "object",
-- "required": [
-- "spaces",
-- "groups"
-- ],
-- "properties": {
-- "spaces": {
-- "type": "array",
-- "items": {
-- "$ref": "#/components/schemas/space.forum-space.v1"
-- }
-- },
-- "groups": {
-- "type": "array",
-- "items": {
-- "type": "object",
-- "required": [
-- "slug",
-- "name",
-- "spaces"
-- ],
-- "properties": {
-- "slug": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "name": {
-- "type": "string"
-- },
-- "description": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "spaces": {
-- "type": "array"
-- }
-- }
-- }
-- }
-- }
-- },
-- {
-- "type": "object",
-- "required": [
-- "space"
-- ],
-- "properties": {
-- "space": {
-- "$ref": "#/components/schemas/space.forum-space.v1"
-- }
-- }
-- },
-- {
-- "type": "string",
-- "contentMediaType": "text/html",
-- "description": "GET /s: the board index page."
-- }
-- ]
-- },
-- "space.forum-space.v1": {
-- "title": "SpaceForumSpace",
-- "description": "space.forum-space@1: one forum space as OpenVibe.Space's /api/v1/spaces API shows it (server/forum/service.js shapeSpace): its slug, name, visibility, thread kind and statuses, style, whether votes and reactions are on, its group and parent board, the members-only gate and, in lists, its counts and last activity.",
-- "type": "object",
-- "required": [
-- "slug",
-- "name",
-- "visibility",
-- "url",
-- "thread_kind",
-- "statuses",
-- "style",
-- "votes",
-- "reactions",
-- "group",
-- "parent",
-- "members_only"
-- ],
-- "properties": {
-- "slug": {
-- "type": "string"
-- },
-- "name": {
-- "type": "string"
-- },
-- "description": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "visibility": {
-- "enum": [
-- "public",
-- "members",
-- "staff"
-- ]
-- },
-- "url": {
-- "type": "string"
-- },
-- "thread_kind": {
-- "type": "string"
-- },
-- "statuses": {
-- "type": "array",
-- "items": {
-- "type": "string"
-- }
-- },
-- "style": {
-- "enum": [
-- "feed",
-- "forum"
-- ]
-- },
-- "votes": {
-- "type": "boolean"
-- },
-- "reactions": {
-- "type": "boolean"
-- },
-- "group": {
-- "type": [
-- "object",
-- "null"
-- ],
-- "properties": {
-- "slug": {
-- "type": "string"
-- },
-- "name": {
-- "type": "string"
-- }
-- }
-- },
-- "parent": {
-- "type": [
-- "object",
-- "null"
-- ],
-- "properties": {
-- "slug": {
-- "type": "string"
-- },
-- "name": {
-- "type": "string"
-- }
-- }
-- },
-- "post_count": {
-- "type": "integer"
-- },
-- "thread_count": {
-- "type": "integer"
-- },
-- "last_activity_at": {
-- "type": [
-- "string",
-- "null"
-- ],
-- "format": "date-time"
-- },
-- "members_only": {
-- "type": [
-- "object",
-- "null"
-- ],
-- "properties": {
-- "owner": {
-- "type": "string"
-- },
-- "owner_username": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "join_url": {
-- "type": [
-- "string",
-- "null"
-- ]
-- }
-- }
-- },
-- "last_post": {
-- "type": [
-- "object",
-- "null"
-- ]
-- },
-- "children": {
-- "type": "array"
-- }
-- }
-- },
-- "space.forum-thread.v1": {
-- "title": "SpaceForumThread",
-- "description": "space.forum-thread@1: one forum thread as OpenVibe.Space's /api/v1/spaces API shows it (server/forum/service.js shapeThread): where it lives, its kind and status, category, members-only gate, author, origin, pin and lock state, score, reply count, views, pages, the last post and the caller's vote.",
-- "type": "object",
-- "required": [
-- "id",
-- "space",
-- "slug",
-- "title",
-- "url",
-- "kind",
-- "author",
-- "origin",
-- "pinned",
-- "locked",
-- "score",
-- "reply_count",
-- "pages",
-- "created_at",
-- "my_vote"
-- ],
-- "properties": {
-- "id": {
-- "type": "integer"
-- },
-- "space": {
-- "type": "string"
-- },
-- "slug": {
-- "type": "string"
-- },
-- "title": {
-- "type": "string",
-- "maxLength": 200
-- },
-- "url": {
-- "type": "string",
-- "description": "/s/<space>/t/<slug>"
-- },
-- "kind": {
-- "type": "string",
-- "description": "discussion, request, roadmap, …"
-- },
-- "status": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "category": {
-- "type": [
-- "object",
-- "null"
-- ],
-- "properties": {
-- "slug": {
-- "type": "string"
-- },
-- "name": {
-- "type": "string"
-- },
-- "description": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "position": {},
-- "thread_count": {
-- "type": "integer"
-- }
-- }
-- },
-- "members_only": {
-- "type": [
-- "object",
-- "null"
-- ],
-- "properties": {
-- "owner": {
-- "type": "string"
-- },
-- "owner_username": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "join_url": {
-- "type": [
-- "string",
-- "null"
-- ]
-- }
-- }
-- },
-- "author": {
-- "type": [
-- "object",
-- "null"
-- ],
-- "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
-- "properties": {
-- "subject": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "username": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "display_name": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "avatar_url": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "profile_color": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "is_ai": {
-- "const": true
-- },
-- "is_system": {
-- "const": true
-- }
-- }
-- },
-- "origin": {
-- "type": "string"
-- },
-- "pinned": {
-- "type": "boolean"
-- },
-- "locked": {
-- "type": "boolean"
-- },
-- "score": {
-- "type": "integer"
-- },
-- "reply_count": {
-- "type": "integer",
-- "minimum": 0
-- },
-- "views": {
-- "type": "integer",
-- "minimum": 0
-- },
-- "pages": {
-- "type": "integer",
-- "minimum": 1
-- },
-- "last_post": {
-- "type": [
-- "object",
-- "null"
-- ],
-- "properties": {
-- "id": {
-- "type": "integer"
-- },
-- "author": {
-- "type": [
-- "object",
-- "null"
-- ],
-- "description": "The author projection: a person (subject and Network profile), OpenVibe AI (is_ai) or the system (is_system); null when unknown or deleted.",
-- "properties": {
-- "subject": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "username": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "display_name": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "avatar_url": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "profile_color": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "is_ai": {
-- "const": true
-- },
-- "is_system": {
-- "const": true
-- }
-- }
-- }
-- }
-- },
-- "crosspost_of": {},
-- "last_activity_at": {
-- "type": [
-- "string",
-- "null"
-- ],
-- "format": "date-time"
-- },
-- "created_at": {
-- "type": [
-- "string",
-- "null"
-- ],
-- "format": "date-time"
-- },
-- "my_vote": {
-- "enum": [
-- 1,
-- -1,
-- 0
-- ]
-- }
-- }
-- },
-- "space.post-write-request.v1": {
-- "title": "SpacePostWriteRequest",
-- "description": "space.post-write-request@1: bodies of the forum writes on OpenVibe.Space (space.post.write; a service writes as X-OV-Subject, or as AI with X-OV-Origin: ai). POST /api/v1/spaces/:space/threads: { title, body, members_only?, category?, attachments?, pastes? } (title 3 to 200 characters after whitespace is collapsed; body Markdown, non-blank, at most 40000 characters; body_markdown is accepted for body). POST …/threads/:slug/posts (a reply) and PUT /api/v1/posts/:id (an edit): { body, attachments?, pastes? }. POST …/threads/:slug/votes: { value: 1 | -1 | 0 }. DELETE /api/v1/posts/:id and DELETE /api/v1/spaces/:space/threads/:slug take no body. Unknown fields are ignored; refusals are problem+json (post.empty, post.too_long, thread.invalid_title, vote.invalid, thread.locked, vip.members_only, 403 space.blocked, …); see docs/space-problem-codes.md.",
-- "anyOf": [
-- {
-- "type": "object",
-- "anyOf": [
-- {
-- "required": [
-- "body"
-- ]
-- },
-- {
-- "required": [
-- "body_markdown"
-- ]
-- }
-- ],
-- "properties": {
-- "title": {
-- "type": "string",
-- "description": "A new thread's title."
-- },
-- "body": {
-- "type": "string",
-- "minLength": 1,
-- "maxLength": 40000,
-- "description": "Markdown."
-- },
-- "body_markdown": {
-- "type": "string",
-- "minLength": 1,
-- "maxLength": 40000,
-- "description": "Read when body is absent."
-- },
-- "members_only": {
-- "description": "A new thread only: true gates it to the author's own VIP members; { owner: usr_… } (or the subject itself) names the creator (moderators, or the author naming themselves); null or false: open.",
-- "anyOf": [
-- {
-- "type": [
-- "boolean",
-- "null"
-- ]
-- },
-- {
-- "type": "string",
-- "pattern": "^usr_"
-- },
-- {
-- "type": "object",
-- "required": [
-- "owner"
-- ],
-- "properties": {
-- "owner": {
-- "type": "string",
-- "pattern": "^usr_"
-- }
-- }
-- }
-- ]
-- },
-- "category": {
-- "type": "string",
-- "description": "A new thread only: the slug of one of the space's categories."
-- },
-- "attachments": {
-- "description": "At most 4 images the writer uploaded (POST /api/v1/spaces/:space/attachments) and has not used yet, by media id. People only.",
-- "anyOf": [
-- {
-- "type": "string",
-- "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"
-- },
-- {
-- "type": "array",
-- "maxItems": 4,
-- "items": {
-- "type": "string",
-- "pattern": "^med_[0-9A-HJKMNP-TV-Z]{26}$"
-- }
-- }
-- ]
-- },
-- "pastes": {
-- "description": "At most 4 shareable pastes (not private, not burn-after-read) by slug or /p/<slug> link: an array, or one string separated by spaces or commas.",
-- "type": [
-- "array",
-- "string"
-- ],
-- "items": {
-- "type": "string"
-- }
-- }
-- }
-- },
-- {
-- "type": "object",
-- "required": [
-- "value"
-- ],
-- "properties": {
-- "value": {
-- "enum": [
-- 1,
-- -1,
-- 0,
-- "1",
-- "-1",
-- "0"
-- ],
-- "description": "A thread vote; 0 takes the vote back."
-- }
-- }
-- },
-- {
-- "$ref": "#/components/schemas/common.no-body.v1"
-- }
-- ]
-- },
-- "space.post-write-result.v1": {
-- "title": "SpacePostWriteResult",
-- "description": "space.post-write-result@1: answers of the forum writes on OpenVibe.Space (space.post.write). POST /api/v1/spaces/:space/threads → 201 { thread, post } (the thread and its opening post); POST …/threads/:slug/posts → 201 { post, page, url } (the reply, the page it lands on and its address with #post-<id>); PUT /api/v1/posts/:id → { post }; POST …/threads/:slug/votes → { thread_id, score, upvotes, downvotes, my_vote }; DELETE /api/v1/posts/:id → { ok, id } (deleting the opening post deletes the thread: { ok, id, deleted: thread }); DELETE /api/v1/spaces/:space/threads/:slug → { ok, id, deleted: thread }. Refusals are problem+json.",
-- "anyOf": [
-- {
-- "type": "object",
-- "required": [
-- "thread",
-- "post"
-- ],
-- "properties": {
-- "thread": {
-- "$ref": "#/components/schemas/space.forum-thread.v1"
-- },
-- "post": {
-- "$ref": "#/components/schemas/space.forum-post.v1"
-- }
-- }
-- },
-- {
-- "type": "object",
-- "required": [
-- "post"
-- ],
-- "properties": {
-- "post": {
-- "$ref": "#/components/schemas/space.forum-post.v1"
-- },
-- "page": {
-- "type": "integer",
-- "minimum": 1
-- },
-- "url": {
-- "type": "string"
-- }
-- }
-- },
-- {
-- "type": "object",
-- "required": [
-- "thread_id",
-- "score",
-- "upvotes",
-- "downvotes",
-- "my_vote"
-- ],
-- "properties": {
-- "thread_id": {
-- "type": "integer"
-- },
-- "score": {
-- "type": "integer"
-- },
-- "upvotes": {
-- "type": "integer",
-- "minimum": 0
-- },
-- "downvotes": {
-- "type": "integer",
-- "minimum": 0
-- },
-- "my_vote": {
-- "enum": [
-- 1,
-- -1,
-- 0
-- ]
-- }
-- }
-- },
-- {
-- "type": "object",
-- "required": [
-- "ok",
-- "id"
-- ],
-- "properties": {
-- "ok": {
-- "const": true
-- },
-- "id": {
-- "type": "integer"
-- },
-- "deleted": {
-- "const": "thread"
-- }
-- }
-- }
-- ]
-- },
-- "space.thread-read-result.v1": {
-- "title": "SpaceThreadReadResult",
-- "description": "space.thread-read-result@1: answers of space.thread.read on OpenVibe.Space. GET /api/v1/spaces/:space/threads?sort=&page=&limit=&category=&status= → a page of threads with the space, paging, categories, children and what the viewer may do; GET /api/v1/spaces/:space/threads/:slug?page= → the thread with a page of posts (reactions, author stats, crosspost info, attachments settings, paging, viewer). GET /s/:space and /s/:space/t/:slug are the same as HTML pages; GET /s/feed.xml and /s/:space/feed.xml are RSS feeds of public threads.",
-- "anyOf": [
-- {
-- "type": "object",
-- "required": [
-- "space",
-- "threads",
-- "page",
-- "pages",
-- "total"
-- ],
-- "properties": {
-- "space": {
-- "type": "object"
-- },
-- "sort": {
-- "type": "string"
-- },
-- "page": {
-- "type": "integer",
-- "minimum": 1
-- },
-- "per_page": {
-- "type": "integer"
-- },
-- "total": {
-- "type": "integer",
-- "minimum": 0
-- },
-- "pages": {
-- "type": "integer",
-- "minimum": 1
-- },
-- "categories": {
-- "type": "array"
-- },
-- "category": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "status": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "viewer": {
-- "type": "object"
-- },
-- "children": {
-- "type": "array"
-- },
-- "groups": {
-- "type": "array"
-- },
-- "threads": {
-- "type": "array",
-- "items": {
-- "$ref": "#/components/schemas/space.forum-thread.v1"
-- }
-- }
-- }
-- },
-- {
-- "type": "object",
-- "required": [
-- "space",
-- "thread",
-- "posts",
-- "page",
-- "pages"
-- ],
-- "properties": {
-- "space": {
-- "type": "object"
-- },
-- "thread": {
-- "$ref": "#/components/schemas/space.forum-thread.v1"
-- },
-- "posts": {
-- "type": "array",
-- "items": {
-- "$ref": "#/components/schemas/space.forum-post.v1"
-- }
-- },
-- "reactions": {
-- "type": "array"
-- },
-- "crosspost": {
-- "type": "object"
-- },
-- "categories": {
-- "type": "array"
-- },
-- "attachments": {
-- "type": "object"
-- },
-- "page": {
-- "type": "integer",
-- "minimum": 1
-- },
-- "per_page": {
-- "type": "integer"
-- },
-- "pages": {
-- "type": "integer",
-- "minimum": 1
-- },
-- "total": {
-- "type": "integer"
-- },
-- "viewer": {
-- "type": "object"
-- }
-- }
-- },
-- {
-- "type": "string",
-- "contentMediaType": "text/html",
-- "description": "GET /s/:space and /s/:space/t/:slug: HTML pages."
-- },
-- {
-- "type": "string",
-- "contentMediaType": "application/rss+xml",
-- "description": "GET /s/feed.xml and /s/:space/feed.xml: RSS feeds."
-- }
-- ]
-- }
-- },
-- "securitySchemes": {
-- "openvibeToken": {
-- "type": "http",
-- "scheme": "bearer",
-- "bearerFormat": "JWT",
-- "description": "An OpenVibe Network token for this service: a person's token, an app token (client credentials, audience of the service) or a first-party service token. Its `cap` claim must include the capability the operation names."
-- }
-- }
-- }
--}
ddiff --git a/generated/openapi/tips.json b/generated/openapi/tips.json
iindex b00304af..0d1421fe 100644
---- a/generated/openapi/tips.json
++++ b/generated/openapi/tips.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tips API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/tools.json b/generated/openapi/tools.json
iindex e786f230..494b0851 100644
---- a/generated/openapi/tools.json
++++ b/generated/openapi/tools.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tools API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/trade.json b/generated/openapi/trade.json
iindex 507ac52f..b9199843 100644
---- a/generated/openapi/trade.json
++++ b/generated/openapi/trade.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Trade API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/vip.json b/generated/openapi/vip.json
iindex 19d809ec..1c70adba 100644
---- a/generated/openapi/vip.json
++++ b/generated/openapi/vip.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.VIP API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/watch.json b/generated/openapi/watch.json
iindex a66eac77..272d754c 100644
---- a/generated/openapi/watch.json
++++ b/generated/openapi/watch.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Watch API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/wiki.json b/generated/openapi/wiki.json
iindex b984a1d4..4ba7f20a 100644
---- a/generated/openapi/wiki.json
++++ b/generated/openapi/wiki.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Wiki API",
-- "version": "0.117.0",
-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.117.0): each operation lists the capabilities it performs; a token for 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.118.0",
++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.118.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/manifests/capabilities/community.post.create.json b/manifests/capabilities/community.post.create.json
iindex 3feb375b..879f613d 100644
---- a/manifests/capabilities/community.post.create.json
++++ b/manifests/capabilities/community.post.create.json
@@@ -2,10 +2,10 @@
"id": "community.post.create",
"version": "1.1",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"visibility": "public",
"sensitive": true,
-- "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.",
++ "description": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
"outputSchema": "community.post-write-result@1",
"permissions": [
"community:write"
@@@ -14,7 +14,16 @@
"space_member"
],
"quotaClass": "community-write",
-- "events": [],
-- "implementedBy": [],
++ "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"
++ ],
"inputSchema": "community.post-write-request@1"
}
ddiff --git a/manifests/capabilities/community.space.manage.json b/manifests/capabilities/community.space.manage.json
iindex 26371ddc..3206f61c 100644
---- a/manifests/capabilities/community.space.manage.json
++++ b/manifests/capabilities/community.space.manage.json
@@@ -2,9 +2,9 @@
"id": "community.space.manage",
"version": "1.0",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"visibility": "internal",
-- "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.",
++ "description": "Staff administration of spaces: create, rename, set visibility, and gate a space to one creator's VIP members. Only the members-only gate is served today (checked with community.comment.moderate plus a moderator); spaces are seeded by Community's database setup, and nothing creates or edits them.",
"permissions": [
"community:moderate"
],
@@@ -13,5 +13,7 @@
],
"quotaClass": "community-moderate",
"events": [],
-- "implementedBy": []
++ "implementedBy": [
++ "PUT /api/v1/spaces/:space/members-only"
++ ]
}
ddiff --git a/manifests/capabilities/community.space.read.json b/manifests/capabilities/community.space.read.json
iindex 1dbc43e1..1b61d2f8 100644
---- a/manifests/capabilities/community.space.read.json
++++ b/manifests/capabilities/community.space.read.json
@@@ -2,9 +2,9 @@
"id": "community.space.read",
"version": "1.0",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"visibility": "public",
-- "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.",
++ "description": "List the spaces the caller can open and read one space's metadata (roadmap §30.2 community.space.*). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Community checks space visibility itself.",
"outputSchema": "community.space-read-result@1",
"permissions": [
"community:read"
@@@ -14,6 +14,10 @@
],
"quotaClass": "community-read",
"events": [],
-- "implementedBy": [],
++ "implementedBy": [
++ "GET /api/v1/spaces",
++ "GET /api/v1/spaces/:space",
++ "GET /s"
++ ],
"inputSchema": "common.no-body@1"
}
ddiff --git a/manifests/capabilities/community.thread.read.json b/manifests/capabilities/community.thread.read.json
iindex 1ed9ffc3..f5d9bab4 100644
---- a/manifests/capabilities/community.thread.read.json
++++ b/manifests/capabilities/community.thread.read.json
@@@ -2,9 +2,9 @@
"id": "community.thread.read",
"version": "1.0",
"owner": "community",
-- "status": "retired",
++ "status": "active",
"visibility": "public",
-- "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.",
++ "description": "Read a space's threads (hot, new or top, paged) and one thread with its posts, plus the RSS feeds. Community checks space visibility and the VIP gate (403 vip.members_only). Creating threads and replies stays community.post.create.",
"outputSchema": "community.thread-read-result@1",
"permissions": [
"community:read"
@@@ -14,6 +14,13 @@
],
"quotaClass": "community-read",
"events": [],
-- "implementedBy": [],
++ "implementedBy": [
++ "GET /api/v1/spaces/:space/threads",
++ "GET /api/v1/spaces/:space/threads/:slug",
++ "GET /s/:space",
++ "GET /s/:space/t/:slug",
++ "GET /s/feed.xml",
++ "GET /s/:space/feed.xml"
++ ],
"inputSchema": "common.no-body@1"
}
ddiff --git a/manifests/capabilities/community.vote.set.json b/manifests/capabilities/community.vote.set.json
iindex b130527d..c4ded914 100644
---- a/manifests/capabilities/community.vote.set.json
++++ b/manifests/capabilities/community.vote.set.json
@@@ -4,17 +4,19 @@
"owner": "community",
"status": "planned",
"visibility": "public",
-- "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.",
++ "description": "PLANNED as its own grant: up-vote, down-vote or clear a vote (value 1, -1 or 0; 0 removes it, so there is no separate vote.remove) on a thread or a comment as the acting person (roadmap §30.2 community.vote.set|remove). Locked threads refuse; 60 votes a minute per person. The routes serve production today, guarded by community.post.create (threads) and community.comment.write (comments), which keep covering them.",
"inputSchema": "common.entity-ref@1",
"permissions": [
"community:vote"
],
"resourceConstraints": [
-- "none"
++ "space_member"
],
"quotaClass": "community-vote",
"events": [],
"implementedBy": [
-- "POST /api/v1/comments/:commentId/votes"
++ "POST /api/v1/spaces/:space/threads/:slug/votes",
++ "POST /api/v1/comments/:commentId/votes",
++ "POST /s/:space/t/:slug/vote"
]
}
ddiff --git a/manifests/capabilities/space.forum.manage.json b/manifests/capabilities/space.forum.manage.json
iindex 47c5b7fe..592d6b2f 100644
---- a/manifests/capabilities/space.forum.manage.json
++++ b/manifests/capabilities/space.forum.manage.json
@@@ -2,9 +2,9 @@
"id": "space.forum.manage",
"version": "1.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"visibility": "internal",
-- "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.",
++ "description": "RETIRED at 0.118.0: OpenVibe.Space no longer serves this forum capability. Use community.space.manage on OpenVibe.Community. Space's /s/* paths redirect permanently to openvibe.community.",
"permissions": [
"space:moderate"
],
@@@ -12,16 +12,6 @@
"none"
],
"quotaClass": "space-moderate",
-- "events": [
-- "space.moderation.action"
-- ],
-- "implementedBy": [
-- "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"
-- ]
++ "events": [],
++ "implementedBy": []
}
ddiff --git a/manifests/capabilities/space.forum.read.json b/manifests/capabilities/space.forum.read.json
iindex e557a10c..468b00a3 100644
---- a/manifests/capabilities/space.forum.read.json
++++ b/manifests/capabilities/space.forum.read.json
@@@ -2,9 +2,9 @@
"id": "space.forum.read",
"version": "1.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"visibility": "public",
-- "description": "List the spaces the caller can open and read one space's metadata (renamed from community.space.read, plan T10 D3: OpenVibe.Space owns the forum). Anonymous callers see public spaces, signed-in people also members spaces, moderators also staff spaces; members-only (VIP) spaces are flagged. Served to anyone; Space checks space visibility itself.",
++ "description": "RETIRED at 0.118.0: OpenVibe.Space no longer serves this forum capability. Use community.space.read on OpenVibe.Community. Space's /s/* paths redirect permanently to openvibe.community.",
"outputSchema": "space.forum-read-result@1",
"permissions": [
"space:read"
@@@ -14,13 +14,6 @@
],
"quotaClass": "space-read",
"events": [],
-- "implementedBy": [
-- "GET /api/v1/spaces",
-- "GET /api/v1/spaces/:space",
-- "GET /s",
-- "GET /api/v1/spaces/:space/categories",
-- "GET /api/v1/spaces/:space/moderators",
-- "GET /api/v1/space-groups"
-- ],
++ "implementedBy": [],
"inputSchema": "common.no-body@1"
}
ddiff --git a/manifests/capabilities/space.post.write.json b/manifests/capabilities/space.post.write.json
iindex 37db01a0..332b88ae 100644
---- a/manifests/capabilities/space.post.write.json
++++ b/manifests/capabilities/space.post.write.json
@@@ -2,10 +2,10 @@
"id": "space.post.write",
"version": "1.1",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"visibility": "public",
"sensitive": true,
-- "description": "Forum writes as a service (renamed from community.post.create, plan T10 D3): create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by OpenVibe.Space.",
++ "description": "RETIRED at 0.118.0: OpenVibe.Space no longer serves this forum capability. Use community.post.create on OpenVibe.Community. Space's /s/* paths redirect permanently to openvibe.community.",
"outputSchema": "space.post-write-result@1",
"permissions": [
"space:write"
@@@ -14,19 +14,7 @@
"space_member"
],
"quotaClass": "space-write",
-- "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",
-- "POST /api/v1/spaces/:space/attachments",
-- "POST /api/v1/spaces/:space/threads/:slug/crosspost",
-- "POST /api/v1/posts/:id/reactions"
-- ],
++ "events": [],
++ "implementedBy": [],
"inputSchema": "space.post-write-request@1"
}
ddiff --git a/manifests/capabilities/space.pulse.read.json b/manifests/capabilities/space.pulse.read.json
iindex 8ff9805b..531cc1ed 100644
---- a/manifests/capabilities/space.pulse.read.json
++++ b/manifests/capabilities/space.pulse.read.json
@@@ -2,9 +2,9 @@
"id": "space.pulse.read",
"version": "1.0",
"owner": "space",
-- "status": "planned",
++ "status": "retired",
"visibility": "public",
-- "description": "PLANNED: read the network Pulse activity OpenVibe.Space shows beside its forums (plan T10 D3). Community keeps the Pulse store and its own community.pulse.read/write; Space reads the same public feed for its pages. Not built.",
++ "description": "RETIRED at 0.118.0: OpenVibe.Space keeps no forum. Pulse is read through community.pulse.read on OpenVibe.Community.",
"permissions": [
"space:read"
],
ddiff --git a/manifests/capabilities/space.thread.read.json b/manifests/capabilities/space.thread.read.json
iindex 4975856c..bb08a7b6 100644
---- a/manifests/capabilities/space.thread.read.json
++++ b/manifests/capabilities/space.thread.read.json
@@@ -2,9 +2,9 @@
"id": "space.thread.read",
"version": "1.0",
"owner": "space",
-- "status": "active",
++ "status": "retired",
"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.",
++ "description": "RETIRED at 0.118.0: OpenVibe.Space no longer serves this forum capability. Use community.thread.read on OpenVibe.Community. Space's /s/* paths redirect permanently to openvibe.community.",
"outputSchema": "space.thread-read-result@1",
"permissions": [
"space:read"
@@@ -14,13 +14,6 @@
],
"quotaClass": "space-read",
"events": [],
-- "implementedBy": [
-- "GET /api/v1/spaces/:space/threads",
-- "GET /api/v1/spaces/:space/threads/:slug",
-- "GET /s/:space",
-- "GET /s/:space/t/:slug",
-- "GET /s/feed.xml",
-- "GET /s/:space/feed.xml"
-- ],
++ "implementedBy": [],
"inputSchema": "common.no-body@1"
}
ddiff --git a/manifests/capabilities/space.thread.write.json b/manifests/capabilities/space.thread.write.json
iindex a7a4ad52..e13a3eb9 100644
---- a/manifests/capabilities/space.thread.write.json
++++ b/manifests/capabilities/space.thread.write.json
@@@ -2,10 +2,10 @@
"id": "space.thread.write",
"version": "1.0",
"owner": "space",
-- "status": "planned",
++ "status": "retired",
"visibility": "public",
"sensitive": true,
-- "description": "PLANNED: thread lifecycle as its own grant on OpenVibe.Space: create, rename, pin, lock and delete a thread (split out of community.post.create, plan T10 D3; space.post.write keeps replies, edits, votes and deletes until this lands). Account-only.",
++ "description": "RETIRED at 0.118.0: OpenVibe.Space keeps no forum. Thread lifecycle is served by community.post.create on OpenVibe.Community.",
"permissions": [
"space:write"
],
ddiff --git a/manifests/products/openvibe.food.json b/manifests/products/openvibe.food.json
iindex ca03055a..f28ad024 100644
---- a/manifests/products/openvibe.food.json
++++ b/manifests/products/openvibe.food.json
@@@ -5,7 +5,7 @@
"icon": "fa-utensils",
"accent": "#fb923c",
"tagline": "What should we eat?",
-- "description": "OpenVibe.Food will bring food together: compare delivery prices across apps, find coupons and budget meal kits, plan meals alone, as a couple or as a group, and turn what is in your cupboard into recipes with the network's AI. The food tools on OpenVibe.Tools will live here too.",
++ "description": "OpenVibe.Food finds food banks, pantries and budget grocers near you from OpenStreetMap, plans meals for one person, a couple or a household for the days ahead with the shopping list and what it costs, and tells you what your cupboard can already make. Next: delivery price comparison, meal kits and planning together.",
"pillars": [
{
"title": "Compare delivery",
@@@ -24,8 +24,30 @@
"text": "Tell it what you have; get ideas that use it."
}
],
++ "faq": [
++ {
++ "question": "Is it free?",
++ "answer": "Yes. Searching for food and reading the food list need no account; a free OpenVibe account keeps your plans and your cupboard."
++ },
++ {
++ "question": "Where do the food banks and stores come from?",
++ "answer": "From OpenStreetMap, the map anyone can edit, under the Open Database License. Every result is credited and links to its OpenStreetMap entry, and answers are cached for a week so OpenStreetMap is asked politely."
++ },
++ {
++ "question": "Are the prices live?",
++ "answer": "No. The food list carries typical prices from budget chains, and every price says so. Live delivery and store prices come with the comparison feature."
++ },
++ {
++ "question": "How does the meal planner work?",
++ "answer": "Tell it how many people, how many days and, if you like, a budget. It picks budget foods that cover each food group, splits the shopping by the cheapest store and lays out a menu for each day."
++ },
++ {
++ "question": "Is the cupboard advice dietary advice?",
++ "answer": "No. It matches what you have against simple recipes written by OpenVibe and says which ones you can make now and which need one or two more things."
++ }
++ ],
"keywords": "openvibe food, compare food delivery prices, meal kits, meal planning, recipe ideas, grocery tools",
-- "launch": "the food tools moving over from OpenVibe.Tools",
++ "launch": "delivery price comparison and shared plans",
"relationships": {
"service": "food"
}
ddiff --git a/manifests/products/openvibe.help.json b/manifests/products/openvibe.help.json
iindex 15b199e3..32324ebc 100644
---- a/manifests/products/openvibe.help.json
++++ b/manifests/products/openvibe.help.json
@@@ -5,7 +5,7 @@
"icon": "fa-life-ring",
"accent": "#4ade80",
"tagline": "Answers, support and a way to reach people.",
-- "description": "OpenVibe.Help will be the help and support center for every OpenVibe site: search answers drawn from the docs, wiki, blog and service status, follow troubleshooting guides, and open a ticket that stays one conversation on the web and by email. A Report Problem button on every site will attach the safe details staff need (release, request and trace ids) and never secrets.",
++ "description": "OpenVibe.Help is the help centre for every OpenVibe site: what each site does and whether it is live, the common questions each one answers about itself, search across all of them, and a support ticket that stays one conversation with the people who run OpenVibe. Next: answers from the docs, wiki and status pages, and a Report Problem button on every site.",
"pillars": [
{
"title": "Grounded answers",
@@@ -24,8 +24,26 @@
"text": "A support widget and help center for sites built on OpenVibe."
}
],
++ "faq": [
++ {
++ "question": "Where do the answers come from?",
++ "answer": "From each OpenVibe site's own description of itself, kept word for word, with a link to where it was read. Nothing is written by an AI."
++ },
++ {
++ "question": "How do I reach a person?",
++ "answer": "Sign in with your OpenVibe account and open a ticket. Replies stay in one conversation that only you and OpenVibe staff can read."
++ },
++ {
++ "question": "Do I need an account to read the help centre?",
++ "answer": "No. Reading and searching are open to everyone; only tickets need an account."
++ },
++ {
++ "question": "Which sites does it cover?",
++ "answer": "Every OpenVibe product site, live or coming, from OpenVibe.Live to OpenRe.Stream. Each one has its own page with its questions and where it lives."
++ }
++ ],
"keywords": "openvibe help, openvibe support, help center, support tickets, troubleshooting",
-- "launch": "the ticket service and the Frame's Help button",
++ "launch": "grounded answers and the Report Problem button",
"relationships": {
"service": "help"
}
ddiff --git a/manifests/products/openvibe.quest.json b/manifests/products/openvibe.quest.json
iindex 4fc9ff27..8f339790 100644
---- a/manifests/products/openvibe.quest.json
++++ b/manifests/products/openvibe.quest.json
@@@ -5,7 +5,7 @@
"icon": "fa-map",
"accent": "#fbbf24",
"tagline": "Quests across the whole network.",
-- "description": "OpenVibe.Quest will be the network's shared quest and rewards system: quests that span every OpenVibe site, from a streamer's getting-started steps on OpenVibe.Live to community challenges, cosmetics and OpenCoins, moddable and open to anyone through the API.",
++ "description": "OpenVibe.Quest is the network's shared quest log: real things you do on OpenVibe sites — going live, following someone, starting a thread, chatting, playing, improving a wiki page — complete quests that pay badges, and OpenCoins once they are switched on. Quest counts only the events the sites already report. Next: OpenCoins rewards, quests streamers and communities write, and quests for OpenVibe.Games.",
"pillars": [
{
"title": "Quests everywhere",
@@@ -24,8 +24,26 @@
"text": "The quest layer for OpenVibe.Games and its creators."
}
],
++ "faq": [
++ {
++ "question": "How does Quest know what I did?",
++ "answer": "The OpenVibe sites already report events such as a stream starting or a thread being posted. Quest counts the ones its quests name, for the person each event names, and nothing else: no page views, no clicks, nothing you wrote."
++ },
++ {
++ "question": "What do quests pay?",
++ "answer": "A badge for each completed quest, shown on your profile across the network. OpenCoins rewards are built and switch on soon."
++ },
++ {
++ "question": "Are OpenCoins money?",
++ "answer": "No. OpenCoins are loyalty points for the OpenVibe network. They are never money, and Quest never pays in Vibes."
++ },
++ {
++ "question": "Can my community make its own quests?",
++ "answer": "Not yet: community quests need moderation tools first. The quest log and its API are already built for them."
++ }
++ ],
"keywords": "openvibe quest, quests, achievements, rewards, gamification, streamer quests",
-- "launch": "the shared quest service",
++ "launch": "OpenCoins rewards and community quests",
"relationships": {
"service": "quest"
}
ddiff --git a/manifests/products/openvibe.rent.json b/manifests/products/openvibe.rent.json
iindex d72363ff..c068b6e0 100644
---- a/manifests/products/openvibe.rent.json
++++ b/manifests/products/openvibe.rent.json
@@@ -5,7 +5,7 @@
"icon": "fa-key",
"accent": "#22d3ee",
"tagline": "Every rental, one search.",
-- "description": "OpenVibe.Rent will bring rentals into one place: apartments, rooms, commercial space, equipment and services from many listing sites, with alerts, AI summaries that link their sources, and one OpenVibe account for your searches.",
++ "description": "OpenVibe.Rent is one place to find and offer things to rent — apartments, rooms, houses, workspace, parking and storage, and equipment — posted by people, with no street addresses, contact details only for signed-in people, reports that hide a listing until staff look, and saved searches. Nothing is scraped and no payments are taken. Next: listing sources whose terms are verified, and alerts.",
"pillars": [
{
"title": "One search",
@@@ -24,8 +24,30 @@
"text": "Saved searches and favourites under your OpenVibe account."
}
],
++ "faq": [
++ {
++ "question": "Who posts the listings?",
++ "answer": "People with an OpenVibe account. In this first version nothing is copied from other listing sites; sources whose terms allow it come later, each named and linked."
++ },
++ {
++ "question": "Does OpenVibe take payments or deposits?",
++ "answer": "No. A listing carries a way to contact the person who posted it and nothing more. Never pay or send a deposit before you have seen the place and signed an agreement."
++ },
++ {
++ "question": "Why can't I add my street address?",
++ "answer": "For your safety: a listing names a city and, if you like, a neighbourhood. A street address is refused."
++ },
++ {
++ "question": "How long does a listing stay up?",
++ "answer": "Thirty days. You can renew it, edit it, hide it or delete it at any time."
++ },
++ {
++ "question": "What happens when a listing is reported?",
++ "answer": "Three reports from different people hide it until OpenVibe staff look at it and restore or remove it."
++ }
++ ],
"keywords": "openvibe rent, rental search, apartments for rent, rooms for rent, commercial rentals, rental alerts",
-- "launch": "verified listing sources and the shared discovery system",
++ "launch": "verified listing sources and alerts",
"relationships": {
"service": "rent"
}
ddiff --git a/manifests/products/openvibe.watch.json b/manifests/products/openvibe.watch.json
iindex 6444ddcd..5ad50adc 100644
---- a/manifests/products/openvibe.watch.json
++++ b/manifests/products/openvibe.watch.json
@@@ -5,7 +5,7 @@
"icon": "fa-eye",
"accent": "#facc15",
"tagline": "Watch anything. Hear about what matters.",
-- "description": "OpenVibe.Watch will watch web pages, feeds, APIs, prices and stock, Git releases, OpenVibe activity, videos, livestreams and your own cameras, pull out what changed or what matters, and tell you, your apps or your AI agents. Checks respect site terms and robots rules, heavy work runs on shared workers, and cameras are read by a small agent on your own network so nothing is exposed to the internet.",
++ "description": "OpenVibe.Watch checks a web page, an RSS or Atom feed or a JSON API on the cadence you pick, pulls out the value you care about, and tells you when it changes or crosses the line you set — politely: it asks sites whether anything changed before reading them, spaces its requests per site and waits when a site asks. Next: events and webhooks, browser checks, prices and releases, cameras on your own network, and actions for your apps and agents.",
"pillars": [
{
"title": "Any source",
@@@ -24,8 +24,30 @@
"text": "Site terms, robots rules and fair limits on every check."
}
],
++ "faq": [
++ {
++ "question": "What can I watch today?",
++ "answer": "A web page (all of it, or the part a CSS selector or a pattern picks), an RSS or Atom feed (for example a GitHub releases feed), or a JSON API and a path into it."
++ },
++ {
++ "question": "How often does it check?",
++ "answer": "Every 15 minutes, hourly, every 6 hours or daily — your choice. It never checks faster than that, backs off when a site fails, and waits when a site asks it to."
++ },
++ {
++ "question": "How will I hear about a change?",
++ "answer": "When your condition fires, a notification arrives on your OpenVibe account, and the watch page shows every check and the value it found."
++ },
++ {
++ "question": "Does it hammer the sites it watches?",
++ "answer": "No. It sends the site's own validators first, so an unchanged page costs no body, keeps a minimum gap between requests to one site, and records every check."
++ },
++ {
++ "question": "Is it free?",
++ "answer": "Yes, while it is in alpha."
++ }
++ ],
"keywords": "website change monitoring, uptime monitoring api, price drop alerts, restock alerts, release monitoring, youtube video analysis, security camera alerts",
-- "launch": "the watch service on the shared queue and workers",
++ "launch": "events, webhooks, browser checks and actions",
"relationships": {
"service": "watch"
}
ddiff --git a/manifests/products/openvibe.work.json b/manifests/products/openvibe.work.json
iindex 6c199de2..e5649d73 100644
---- a/manifests/products/openvibe.work.json
++++ b/manifests/products/openvibe.work.json
@@@ -5,7 +5,7 @@
"icon": "fa-briefcase",
"accent": "#34d399",
"tagline": "Find work, with an agent on your side.",
-- "description": "OpenVibe.Work will gather job listings from open sources with their provenance, let you search them and watch for new ones that fit, and let an agent help you prepare and apply — with your approval at every step. Part of the OpenVibe network, with one account for every site.",
++ "description": "OpenVibe.Work gathers job listings from open job boards (Arbeitnow, Remotive and RemoteOK) into one search, shows a short excerpt with the board named and links every listing back to where you apply, and keeps your saved searches with a count of what is new. Next: alerts through OpenVibe.Watch and an agent that helps you prepare and apply, with your approval at every step.",
"pillars": [
{
"title": "Every listing sourced",
@@@ -24,8 +24,30 @@
"text": "Your profile and history stay yours, exportable any time."
}
],
++ "faq": [
++ {
++ "question": "Where do the jobs come from?",
++ "answer": "From the public job-board APIs of Arbeitnow, Remotive and RemoteOK, read every six hours. Each listing names its board and links to the original, where you read the full listing and apply."
++ },
++ {
++ "question": "Do you copy the whole job post?",
++ "answer": "No. OpenVibe.Work keeps a short plain-text excerpt and sends you to the board for the rest, as the boards ask."
++ },
++ {
++ "question": "Is it free?",
++ "answer": "Yes. Searching needs no account; a free OpenVibe account keeps your saved searches."
++ },
++ {
++ "question": "Can it tell me when new jobs match?",
++ "answer": "Saved searches show how many listings are new since you last looked. Notifications come with OpenVibe.Watch."
++ },
++ {
++ "question": "Will an AI apply for me?",
++ "answer": "Not yet, and never without you: the agent that helps you prepare and apply is coming, and it asks before every step."
++ }
++ ],
"keywords": "openvibe work, job search, job listings, job alerts, ai job application assistant",
-- "launch": "job listings and alerts",
++ "launch": "alerts and the application agent",
"relationships": {
"service": "work"
}
ddiff --git a/manifests/services/community.json b/manifests/services/community.json
iindex ee584449..ca8a06d6 100644
---- a/manifests/services/community.json
++++ b/manifests/services/community.json
@@@ -16,9 +16,13 @@
"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"
],
@@@ -29,7 +33,9 @@
"community.moderation.action",
"community.paste.created",
"community.paste.deleted",
-- "community.paste.updated"
++ "community.paste.updated",
++ "community.post.created",
++ "community.thread.created"
],
"eventsConsumed": [
"network.account.deleted",
@@@ -65,7 +71,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) and the events outbox's send awaited; unsent rows stay in their tables",
++ "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",
"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)"
@@@ -78,7 +84,7 @@
},
{
"kind": "schedule",
-- "what": "the community.profile and search-document scans restart from what changed"
++ "what": "the Roadmap space re-syncs from docs/roadmap/public.json; the community.profile and search-document scans start again from what changed"
},
{
"kind": "consumer",
@@@ -103,5 +109,5 @@
"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."
++ "notes": "Community serves the forum (spaces, threads, posts, votes, categories, moderation and the Discord relay), pastes, comments, Pulse and submissions. The forum returned to Community from OpenVibe.Space (owner decision 2026-10-08): the community.* forum capabilities, contracts and events are active again, the community.space.*/thread.read/post.create routes and the /s pages and feeds serve production, and Space no longer serves a forum."
}
ddiff --git a/manifests/services/deals.json b/manifests/services/deals.json
iindex 48d90e9d..5f190c72 100644
---- a/manifests/services/deals.json
++++ b/manifests/services/deals.json
@@@ -15,9 +15,8 @@
"ready": "/api/ready",
"internalOrigin": "http://127.0.0.1:4840",
"exposure": {
-- "state": "internal",
-- "publicSite": "placeholder",
-- "note": "the domain serves the OpenVibe.Sites placeholder page"
++ "state": "live",
++ "publicSite": "service"
},
"site": {
"name": "Deals",
ddiff --git a/manifests/services/network.json b/manifests/services/network.json
iindex 725706b9..116b47d8 100644
---- a/manifests/services/network.json
++++ b/manifests/services/network.json
@@@ -99,7 +99,6 @@
"news.moderation.action",
"reviews.moderation.action",
"services.moderation.action",
-- "space.moderation.action",
"tips.interaction.moderated",
"tools.moderation.action",
"tools.usage.recorded",
ddiff --git a/manifests/services/quest.json b/manifests/services/quest.json
iindex 2a0c2d0c..81dd062c 100644
---- a/manifests/services/quest.json
++++ b/manifests/services/quest.json
@@@ -20,10 +20,11 @@
"eventsConsumed": [
"chat.room.message.created",
"community.comment.created",
++ "community.post.created",
++ "community.thread.created",
"games.player.joined",
"live.stream.started",
"network.follow.created",
-- "space.thread.created",
"tools.job.created",
"wiki.revision.created"
],
@@@ -78,7 +79,7 @@
"none": "no claims: one process serves the site"
}
},
-- "notes": "Port 4980, unit openvibe-quest.service. The network's shared quest log: first-party quests counted from domain events OpenVibe.Events delivers to POST /internal/events (signature v2 under QUEST_EVENTS_SECRET, loopback only; Quest creates its own subscriptions at boot with events.subscription.manage), the person taken from the field each event's own schema defines. It also counts community.thread.created and community.post.created, which return with the forum to OpenVibe.Community (T20). A completion awards its badge once and, when QUEST_COINS=on, credits OpenCoins through OpenVibe.Network (network.coins.credit, identity.subject.resolve; caps per person and global per day). Rewards are never Vibes. Consumes OpenVibe.Network (SSO with PKCE S256 as OAuth client quest, JWKS) and OpenVibe.Events. Owns quest_progress, quest_completions, quest_badges, quest_event_inbox.",
++ "notes": "Port 4980, unit openvibe-quest.service. The network's shared quest log: first-party quests counted from domain events OpenVibe.Events delivers to POST /internal/events (signature v2 under QUEST_EVENTS_SECRET, loopback only; Quest creates its own subscriptions at boot with events.subscription.manage), the person taken from the field each event's own schema defines. It also counts community.thread.created and community.post.created, which OpenVibe.Community produces. A completion awards its badge once and, when QUEST_COINS=on, credits OpenCoins through OpenVibe.Network (network.coins.credit, identity.subject.resolve; caps per person and global per day). Rewards are never Vibes. Consumes OpenVibe.Network (SSO with PKCE S256 as OAuth client quest, JWKS) and OpenVibe.Events. Owns quest_progress, quest_completions, quest_badges, quest_event_inbox.",
"health": "/api/health",
"ready": "/api/ready",
"internalOrigin": "http://127.0.0.1:4980",
ddiff --git a/manifests/services/space.json b/manifests/services/space.json
iindex 071271ef..28890da6 100644
---- a/manifests/services/space.json
++++ b/manifests/services/space.json
@@@ -18,53 +18,37 @@
"site": {
"name": "Space",
"icon": "space",
-- "tagline": "Forums, boards and threads",
-- "what": "OpenVibe's forums: public and members-only spaces, threads, posts, votes, categories and the Discord relay, moved off Community (plan T10, D1/D2).",
++ "tagline": "Code hosting and spaces",
++ "what": "OpenVibe's code hosting, and \"spaces\": apps that use your OpenVibe account and the SDK, coming next. The forum returned to OpenVibe.Community.",
"legalProfile": "ugc",
"position": 19,
"tld": "space",
"accent": "#818cf8",
-- "description": "OpenVibe.Space is the network forum: browse public and members-only boards, start threads, reply, vote and moderate spaces with one OpenVibe identity.",
++ "description": "OpenVibe.Space will host code and \"spaces\" — apps that use your OpenVibe account and the SDK — and both are still being built. Until they land it serves a home, and its former /s/* forum paths redirect permanently to openvibe.community.",
"pillars": [
{
-- "title": "Any forum",
-- "text": "Browse boards for discussions, requests and the roadmap."
++ "title": "Code hosting",
++ "text": "The network's code, hosted here."
},
{
-- "title": "Classic depth",
-- "text": "Child boards, threads, categories and moderation tools."
++ "title": "Spaces",
++ "text": "Apps that use your OpenVibe account and the SDK."
},
{
-- "title": "Make it yours",
-- "text": "Moderators can shape boards, categories and visibility."
++ "title": "Coming next",
++ "text": "Neither is built yet; Space serves a home until they land."
},
{
"title": "One account",
"text": "Your OpenVibe identity, everywhere on the network."
}
],
-- "keywords": "openvibe space, forums, create a forum, community boards, xenforo alternative, phpbb alternative",
-- "launch": "Forums moved from OpenVibe.Community to OpenVibe.Space (plan T10 step 3)."
++ "keywords": "openvibe space, code hosting, openvibe spaces, openvibe account, openvibe sdk",
++ "launch": "Code hosting and spaces are still to be built; the forum moved back to OpenVibe.Community (2026-10-08)."
},
-- "capabilities": [
-- "space.forum.read",
-- "space.forum.manage",
-- "space.thread.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"
-- ],
++ "capabilities": [],
++ "eventsProduced": [],
++ "eventsConsumed": [],
"namespacesOwned": [
"space.*"
],
@@@ -78,7 +62,6 @@
"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"
@@@ -86,16 +69,8 @@
"startupRecovery": {
"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"
++ "kind": "state",
++ "what": "OpenVibe.Network's signing keys are fetched again at boot; sign-in lives in the browser's cookies, so nothing else resumes (Space consumes no events since the forum returned to Community)"
}
]
},
@@@ -106,17 +81,17 @@
],
"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"
++ "none": "Space's own PostgreSQL migrations only create its tables, so an earlier release reads nothing it cannot handle"
}
},
"contracts": {
"range": ">=0.82.0 <1.0.0"
},
"leases": {
-- "none": "PostgreSQL outbox workers coordinate delivery; no separate service-owned lease is exposed"
++ "none": "no claims: one Space process runs and owns its database"
}
},
-- "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.",
++ "notes": "Space is OpenVibe's code hosting, and \"spaces\" — apps that use your OpenVibe account and the SDK — come next. The forum returned to OpenVibe.Community (owner decision 2026-10-08), so Space serves a home and its former /s/* forum paths redirect permanently to openvibe.community; it keeps no forum capability, contract or event.",
"health": "/api/health",
"ready": "/api/ready",
"internalOrigin": "http://127.0.0.1:4940"
ddiff --git a/package-lock.json b/package-lock.json
iindex e73d2792..b0ffe9d5 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-contracts",
-- "version": "0.115.0",
++ "version": "0.118.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-contracts",
-- "version": "0.115.0",
++ "version": "0.118.0",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
ddiff --git a/package.json b/package.json
iindex 000d5bf8..cc4d7b80 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.117.0",
++ "version": "0.118.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",
ddiff --git a/test/run.js b/test/run.js
iindex 829f105d..291a8a66 100644
---- a/test/run.js
++++ b/test/run.js
@@@ -468,7 +468,7 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
const 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.thread.read': 'active',
++ 'community.vote.set': 'planned', 'community.pulse.read': 'active', 'community.space.read': 'active', 'community.thread.read': 'active',
'search.query.run': 'active',
};
for (const [id, status] of Object.entries(gaps)) {
@@@ -477,13 +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 === '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`);
++ ok(capabilities.get('community.post.create').status === 'active' && capabilities.get('community.post.create').implementedBy.includes('POST /api/v1/spaces/:space/threads'), 'Community serves the forum writes again');
++ ok(capabilities.get('community.space.manage').status === 'active' && capabilities.get('community.space.manage').implementedBy.includes('PUT /api/v1/spaces/:space/members-only'), 'community.space.manage is served again, and names the one route it has');
++ for (const id of ['community.space.read', 'community.space.manage', 'community.thread.read', 'community.post.create']) ok(capabilities.get(id).status === 'active' && services.get('community').capabilities.includes(id), `${id} is served by Community again`);
++ for (const id of ['space.forum.read', 'space.forum.manage', 'space.thread.read', 'space.post.write', 'space.thread.write', 'space.pulse.read']) ok(capabilities.get(id).status === 'retired' && !services.get('space').capabilities.includes(id), `${id} retired from Space`);
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/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(/community\.post\.create/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 === 3, 'community.vote.set names the thread and comment vote routes and the grants that cover them today');
ok(capabilities.get('live.owner.resolve').implementedBy.length === 0 && capabilities.get('live.owner.resolve').outputSchema === 'lineage.resolution@1', 'live.owner.resolve has no public route yet and answers lineage.resolution@1');
ok(JSON.stringify(capabilities.get('search.query.run').implementedBy) === JSON.stringify(capabilities.get('search.query.delegate').implementedBy), 'search.query.run and search.query.delegate are the same routes, asked anonymously or for a person');
const pub = services.get('publishing');
@@@ -805,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', 'services', 'space']) {
++ for (const svc of ['media', 'tools', 'games', 'wiki', 'blog', 'news', 'reviews', 'deals', 'coupons', 'trade', 'services']) {
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}`);
ddiff --git a/test/space-release.test.js b/test/space-release.test.js
iindex 257aafa0..ef993f19 100644
---- a/test/space-release.test.js
++++ b/test/space-release.test.js
@@@ -7,43 +7,84 @@ 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', () => {
++test('Community serves its forum grants again and Space keeps no forum', () => {
const space = contracts.services.get('space');
const community = contracts.services.get('community');
++ assert.equal(community.exposure.state, 'live');
++ assert.deepEqual(community.capabilities, [
++ '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',
++ ]);
++ assert.equal(contracts.capabilities.get('community.post.create').status, 'active');
++ assert.ok(contracts.capabilities.get('community.post.create').implementedBy.includes('POST /api/v1/spaces/:space/threads'));
++ assert.ok(contracts.capabilities.get('community.thread.read').implementedBy.includes('GET /s/:space/t/:slug'));
++ for (const id of ['community.space.read', 'community.space.manage', 'community.thread.read', 'community.post.create']) {
++ assert.equal(contracts.capabilities.get(id).status, 'active');
++ assert.ok(community.capabilities.includes(id));
++ assert.equal(contracts.capabilities.check({ cap: [id] }, id).allowed, true, id);
++ }
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.deepEqual(space.capabilities, []);
++ assert.deepEqual(space.eventsProduced, []);
++ for (const id of ['space.forum.read', 'space.forum.manage', 'space.thread.read', 'space.post.write', 'space.thread.write', 'space.pulse.read']) {
assert.equal(contracts.capabilities.get(id).status, 'retired');
-- assert.ok(!community.capabilities.includes(id));
++ assert.ok(!space.capabilities.includes(id));
assert.equal(contracts.capabilities.check({ cap: [id] }, id).code, 'capability.unknown');
}
++ assert.match(space.site.what, /code hosting/i);
++ assert.match(space.notes, /forum returned to OpenVibe\.Community \(owner decision 2026-10-08\)/);
});
--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');
++test('Community event payloads validate and match the producer', () => {
++ const community = contracts.services.get('community');
++ for (const id of ['community.thread.created', 'community.post.created']) {
++ assert.ok(community.eventsProduced.includes(id));
++ assert.equal(contracts.catalog.find(c => c.id === id).owner, 'community');
++ assert.equal(contracts.catalog.find(c => c.id === id).status, 'active');
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 };
++ const envelope = { event_id: 'evt_01JAB2C3D4E5F6G7H8J9K0MNPQ', event_type: id, version: 1, source: 'community', actor: { type: 'service', id: 'community' }, timestamp: '2026-10-08T00: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']) {
++ for (const id of ['space.thread.created', 'space.post.created', 'space.moderation.action']) {
++ assert.equal(contracts.catalog.find(c => c.id === id).status, 'retired');
++ assert.ok(!contracts.services.get('space').eventsProduced.includes(id));
++ }
++});
++
++test('The retired Space forum contracts name their Community replacements', () => {
++ const deprecations = require('../compatibility/deprecations.json').deprecations;
++ const moves = [
++ ['space.forum-space', 'community.forum-space@1'],
++ ['space.forum-thread', 'community.forum-thread@1'],
++ ['space.forum-post', 'community.forum-post@1'],
++ ['space.forum-read-result', 'community.space-read-result@1'],
++ ['space.thread-read-result', 'community.thread-read-result@1'],
++ ['space.post-write-request', 'community.post-write-request@1'],
++ ['space.post-write-result', 'community.post-write-result@1'],
++ ['space.thread.created', 'community.thread.created@1'],
++ ['space.post.created', 'community.post.created@1'],
++ ['space.moderation.action', 'community.moderation.action@1'],
++ ];
++ for (const [id, replacement] of moves) {
++ const d = deprecations.find(x => x.id === id);
++ assert.ok(d, `${id} is recorded`);
++ assert.equal(d.replacement, replacement);
++ assert.equal(d.since, '0.118.0');
++ assert.match(d.reason, /the forum returned to OpenVibe\.Community \(2026-10-08\)/);
assert.equal(contracts.catalog.find(c => c.id === id).status, 'retired');
-- assert.ok(!contracts.services.get('community').eventsProduced.includes(id));
++ assert.ok(contracts.resolve(replacement), `${id} -> ${replacement} resolves`);
++ }
++ for (const id of ['community.forum-space', 'community.forum-thread', 'community.forum-post', 'community.thread.created', 'community.post.created']) {
++ assert.ok(!deprecations.some(x => x.id === id), `${id} is no longer deprecated`);
}
});