openvibers/openvibe.contracts Commits

Community forum routes move to /forum (#326)

OP OpenVibers committed 7 days ago · a012f6c4a5 · 22 files, +98 −98

The forum moved from /s to /forum and a thread dropped its /t/ segment
(design 2026-11-07). community.thread.read and community.vote.set name the new
HTML routes, the thread schema and the read-result descriptions say
/forum/<space>/<slug>, and the Community fixtures carry the new addresses.
Generated OpenAPI and TypeScript follow (node scripts/generate.js).

Changes

ddiff --git a/contracts/community/forum-thread.v1.json b/contracts/community/forum-thread.v1.json
iindex c5cc0051..1ad88018 100644
---- a/contracts/community/forum-thread.v1.json
++++ b/contracts/community/forum-thread.v1.json
@@@ -37,7 +37,7 @@
},
"url": {
"type": "string",
-- "description": "/s/<space>/t/<slug>"
++ "description": "/forum/<space>/<slug>"
},
"kind": {
"type": "string",
ddiff --git a/contracts/community/thread-read-result.v1.json b/contracts/community/thread-read-result.v1.json
iindex f093ada2..a0bf4848 100644
---- a/contracts/community/thread-read-result.v1.json
++++ b/contracts/community/thread-read-result.v1.json
@@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/thread-read-result.v1.json",
"title": "CommunityThreadReadResult",
-- "description": "community.thread-read-result@1: answers of community.thread.read on OpenVibe.Community. GET /api/v1/spaces/:space/threads?sort=&page=&limit=&category=&status= → a page of threads with the space, paging, categories, children and what the viewer may do; GET /api/v1/spaces/:space/threads/:slug?page= → the thread with a page of posts (reactions, author stats, crosspost info, attachments settings, paging, viewer). GET /s/:space and /s/:space/t/:slug are the same as HTML pages; GET /s/feed.xml and /s/:space/feed.xml are RSS feeds of public threads.",
++ "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 /forum/:space and /forum/:space/:slug are the same as HTML pages; GET /forum/feed.xml and /forum/:space/feed.xml are RSS feeds of public threads.",
"anyOf": [
{
"type": "object",
@@@ -123,12 +123,12 @@
{
"type": "string",
"contentMediaType": "text/html",
-- "description": "GET /s/:space and /s/:space/t/:slug: HTML pages."
++ "description": "GET /forum/:space and /forum/:space/:slug: HTML pages."
},
{
"type": "string",
"contentMediaType": "application/rss+xml",
-- "description": "GET /s/feed.xml and /s/:space/feed.xml: RSS feeds."
++ "description": "GET /forum/feed.xml and /forum/:space/feed.xml: RSS feeds."
}
]
}
ddiff --git a/fixtures/community.forum-space/invalid/bad-style.json b/fixtures/community.forum-space/invalid/bad-style.json
iindex f2eade33..953b736f 100644
---- a/fixtures/community.forum-space/invalid/bad-style.json
++++ b/fixtures/community.forum-space/invalid/bad-style.json
@@@ -1 +1 @@
--{ "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "wiki", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }
++{ "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/forum/streaming", "thread_kind": "discussion", "statuses": [], "style": "wiki", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }
ddiff --git a/fixtures/community.forum-space/valid/streaming.json b/fixtures/community.forum-space/valid/streaming.json
iindex 3950e255..0545dc72 100644
---- a/fixtures/community.forum-space/valid/streaming.json
++++ b/fixtures/community.forum-space/valid/streaming.json
@@@ -1 +1 @@
--{ "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }
++{ "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/forum/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }
ddiff --git a/fixtures/community.forum-thread/invalid/bad-vote.json b/fixtures/community.forum-thread/invalid/bad-vote.json
iindex 3e5f903f..0b645f6e 100644
---- a/fixtures/community.forum-thread/invalid/bad-vote.json
++++ b/fixtures/community.forum-thread/invalid/bad-vote.json
@@@ -1 +1 @@
--{ "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/s/streaming/t/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 2 }
++{ "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/forum/streaming/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 2 }
ddiff --git a/fixtures/community.forum-thread/valid/new.json b/fixtures/community.forum-thread/valid/new.json
iindex 50a44c2a..7256ef03 100644
---- a/fixtures/community.forum-thread/valid/new.json
++++ b/fixtures/community.forum-thread/valid/new.json
@@@ -1 +1 @@
--{ "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/s/streaming/t/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 0 }
++{ "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/forum/streaming/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 0 }
ddiff --git a/fixtures/community.index_document.upserted/valid/thread.json b/fixtures/community.index_document.upserted/valid/thread.json
iindex e4d7f99a..9b898809 100644
---- a/fixtures/community.index_document.upserted/valid/thread.json
++++ b/fixtures/community.index_document.upserted/valid/thread.json
@@@ -1 +1 @@
--{ "owner": "community", "type": "thread", "id": "12", "revision": 1, "deleted": false, "visibility": "public", "canonical_url": "https://openvibe.community/s/general/t/welcome", "title": "Welcome", "summary": "Say hi.", "facets": { "space": "general" }, "authorship": "human", "publication_state": "published", "indexability": { "decision": "index" } }
++{ "owner": "community", "type": "thread", "id": "12", "revision": 1, "deleted": false, "visibility": "public", "canonical_url": "https://openvibe.community/forum/general/welcome", "title": "Welcome", "summary": "Say hi.", "facets": { "space": "general" }, "authorship": "human", "publication_state": "published", "indexability": { "decision": "index" } }
ddiff --git a/fixtures/community.post-write-result/valid/new-thread.json b/fixtures/community.post-write-result/valid/new-thread.json
iindex 413983e9..45adef85 100644
---- a/fixtures/community.post-write-result/valid/new-thread.json
++++ b/fixtures/community.post-write-result/valid/new-thread.json
@@@ -1 +1 @@
--{ "thread": { "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/s/streaming/t/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 0 }, "post": { "attachments": [], "pastes": [ { "slug": "k3JfQ2", "title": "ffmpeg restream flags", "language": "text", "type": "paste", "url": "/p/k3JfQ2", "screenshot_url": null, "lines": 1, "excerpt": "ffmpeg -re -i in.flv -c copy -f flv rtmp://example/live" } ], "id": 5120, "thread_id": 311, "is_opening": true, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "body_markdown": "What bitrates do you use?", "body_html": "<p>What bitrates do you use?</p>", "revision": 1, "deleted": false, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "can_edit": true } }
++{ "thread": { "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/forum/streaming/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 0 }, "post": { "attachments": [], "pastes": [ { "slug": "k3JfQ2", "title": "ffmpeg restream flags", "language": "text", "type": "paste", "url": "/p/k3JfQ2", "screenshot_url": null, "lines": 1, "excerpt": "ffmpeg -re -i in.flv -c copy -f flv rtmp://example/live" } ], "id": 5120, "thread_id": 311, "is_opening": true, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "body_markdown": "What bitrates do you use?", "body_html": "<p>What bitrates do you use?</p>", "revision": 1, "deleted": false, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "can_edit": true } }
ddiff --git a/fixtures/community.post-write-result/valid/reply.json b/fixtures/community.post-write-result/valid/reply.json
iindex e728e59a..6e0c9d7b 100644
---- a/fixtures/community.post-write-result/valid/reply.json
++++ b/fixtures/community.post-write-result/valid/reply.json
@@@ -1 +1 @@
--{ "post": { "attachments": [], "pastes": [ { "slug": "k3JfQ2", "title": "ffmpeg restream flags", "language": "text", "type": "paste", "url": "/p/k3JfQ2", "screenshot_url": null, "lines": 1, "excerpt": "ffmpeg -re -i in.flv -c copy -f flv rtmp://example/live" } ], "id": 5120, "thread_id": 311, "is_opening": false, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "body_markdown": "What bitrates do you use?", "body_html": "<p>What bitrates do you use?</p>", "revision": 1, "deleted": false, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "can_edit": true }, "page": 1, "url": "/s/streaming/t/restreaming-to-three-platforms#post-5121" }
++{ "post": { "attachments": [], "pastes": [ { "slug": "k3JfQ2", "title": "ffmpeg restream flags", "language": "text", "type": "paste", "url": "/p/k3JfQ2", "screenshot_url": null, "lines": 1, "excerpt": "ffmpeg -re -i in.flv -c copy -f flv rtmp://example/live" } ], "id": 5120, "thread_id": 311, "is_opening": false, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "body_markdown": "What bitrates do you use?", "body_html": "<p>What bitrates do you use?</p>", "revision": 1, "deleted": false, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "can_edit": true }, "page": 1, "url": "/forum/streaming/restreaming-to-three-platforms#post-5121" }
ddiff --git a/fixtures/community.post.created/invalid/carries-content.json b/fixtures/community.post.created/invalid/carries-content.json
iindex 291e7be9..1804733e 100644
---- a/fixtures/community.post.created/invalid/carries-content.json
++++ b/fixtures/community.post.created/invalid/carries-content.json
@@@ -7,6 +7,6 @@
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
},
"visibility": "public",
-- "url": "https://openvibe.community/s/general/7#p31",
++ "url": "https://openvibe.community/forum/general/7#p31",
"body": "the content never travels"
}
ddiff --git a/fixtures/community.post.created/valid/example.json b/fixtures/community.post.created/valid/example.json
iindex c70a6b48..510d28e2 100644
---- a/fixtures/community.post.created/valid/example.json
++++ b/fixtures/community.post.created/valid/example.json
@@@ -7,5 +7,5 @@
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
},
"visibility": "public",
-- "url": "https://openvibe.community/s/general/7#p31"
++ "url": "https://openvibe.community/forum/general/7#p31"
}
ddiff --git a/fixtures/community.space-read-result/valid/index.json b/fixtures/community.space-read-result/valid/index.json
iindex 8f01f4f6..1ec4a9ea 100644
---- a/fixtures/community.space-read-result/valid/index.json
++++ b/fixtures/community.space-read-result/valid/index.json
@@@ -1 +1 @@
--{ "spaces": [ { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" } ], "groups": [ { "slug": "make", "name": "Make things", "description": null, "spaces": [ { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" } ] } ] }
++{ "spaces": [ { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/forum/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" } ], "groups": [ { "slug": "make", "name": "Make things", "description": null, "spaces": [ { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/forum/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" } ] } ] }
ddiff --git a/fixtures/community.space-read-result/valid/one.json b/fixtures/community.space-read-result/valid/one.json
iindex 01e00034..737ac9ba 100644
---- a/fixtures/community.space-read-result/valid/one.json
++++ b/fixtures/community.space-read-result/valid/one.json
@@@ -1 +1 @@
--{ "space": { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" } }
++{ "space": { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/forum/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" } }
ddiff --git a/fixtures/community.thread-read-result/valid/thread.json b/fixtures/community.thread-read-result/valid/thread.json
iindex fc86cd4c..7e79fc61 100644
---- a/fixtures/community.thread-read-result/valid/thread.json
++++ b/fixtures/community.thread-read-result/valid/thread.json
@@@ -1 +1 @@
--{ "space": { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }, "thread": { "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/s/streaming/t/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 0 }, "posts": [ { "attachments": [], "pastes": [ { "slug": "k3JfQ2", "title": "ffmpeg restream flags", "language": "text", "type": "paste", "url": "/p/k3JfQ2", "screenshot_url": null, "lines": 1, "excerpt": "ffmpeg -re -i in.flv -c copy -f flv rtmp://example/live" } ], "id": 5120, "thread_id": 311, "is_opening": true, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "body_markdown": "What bitrates do you use?", "body_html": "<p>What bitrates do you use?</p>", "revision": 1, "deleted": false, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "can_edit": true } ], "reactions": [], "crosspost": { "from": null, "to": [], "targets": [] }, "categories": [], "attachments": { "enabled": true, "max": 4, "max_bytes": 8388608 }, "page": 1, "per_page": 20, "pages": 1, "total": 1, "viewer": { "signed_in": true } }
++{ "space": { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/forum/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }, "thread": { "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/forum/streaming/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 0 }, "posts": [ { "attachments": [], "pastes": [ { "slug": "k3JfQ2", "title": "ffmpeg restream flags", "language": "text", "type": "paste", "url": "/p/k3JfQ2", "screenshot_url": null, "lines": 1, "excerpt": "ffmpeg -re -i in.flv -c copy -f flv rtmp://example/live" } ], "id": 5120, "thread_id": 311, "is_opening": true, "origin": "user", "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "body_markdown": "What bitrates do you use?", "body_html": "<p>What bitrates do you use?</p>", "revision": 1, "deleted": false, "created_at": "2026-09-25T18:00:00.000Z", "updated_at": "2026-09-25T18:00:00.000Z", "can_edit": true } ], "reactions": [], "crosspost": { "from": null, "to": [], "targets": [] }, "categories": [], "attachments": { "enabled": true, "max": 4, "max_bytes": 8388608 }, "page": 1, "per_page": 20, "pages": 1, "total": 1, "viewer": { "signed_in": true } }
ddiff --git a/fixtures/community.thread-read-result/valid/threads.json b/fixtures/community.thread-read-result/valid/threads.json
iindex 9e026119..d5d235cb 100644
---- a/fixtures/community.thread-read-result/valid/threads.json
++++ b/fixtures/community.thread-read-result/valid/threads.json
@@@ -1 +1 @@
--{ "space": { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/s/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }, "sort": "hot", "page": 1, "per_page": 25, "total": 1, "pages": 1, "categories": [], "category": null, "status": null, "viewer": { "can_start": true, "can_moderate": false, "signed_in": true }, "children": [], "threads": [ { "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/s/streaming/t/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 0 } ] }
++{ "space": { "slug": "streaming", "name": "Streaming", "description": "Encoders, bitrates and restreams.", "visibility": "public", "url": "/forum/streaming", "thread_kind": "discussion", "statuses": [], "style": "feed", "votes": true, "reactions": true, "group": { "slug": "make", "name": "Make things" }, "parent": null, "members_only": null, "thread_count": 42, "last_activity_at": "2026-09-25T18:00:00.000Z" }, "sort": "hot", "page": 1, "per_page": 25, "total": 1, "pages": 1, "categories": [], "category": null, "status": null, "viewer": { "can_start": true, "can_moderate": false, "signed_in": true }, "children": [], "threads": [ { "id": 311, "space": "streaming", "slug": "restreaming-to-three-platforms", "title": "Restreaming to three platforms at once", "url": "/forum/streaming/restreaming-to-three-platforms", "kind": "discussion", "status": null, "category": { "slug": "help", "name": "Help", "description": null, "position": 1 }, "members_only": null, "author": { "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "username": "alice", "display_name": "Alice", "avatar_url": null, "profile_color": "#3b82f6" }, "origin": "user", "pinned": false, "locked": false, "score": 0, "reply_count": 0, "views": 0, "pages": 1, "last_post": null, "crosspost_of": null, "last_activity_at": "2026-09-25T18:00:00.000Z", "created_at": "2026-09-25T18:00:00.000Z", "my_vote": 0 } ] }
ddiff --git a/fixtures/community.thread.created/invalid/carries-content.json b/fixtures/community.thread.created/invalid/carries-content.json
iindex 4f2d9708..478080b2 100644
---- a/fixtures/community.thread.created/invalid/carries-content.json
++++ b/fixtures/community.thread.created/invalid/carries-content.json
@@@ -6,6 +6,6 @@
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
},
"visibility": "public",
-- "url": "https://openvibe.community/s/general/7",
++ "url": "https://openvibe.community/forum/general/7",
"body": "the content never travels"
}
ddiff --git a/fixtures/community.thread.created/valid/example.json b/fixtures/community.thread.created/valid/example.json
iindex 32ecfbe5..91c0a276 100644
---- a/fixtures/community.thread.created/valid/example.json
++++ b/fixtures/community.thread.created/valid/example.json
@@@ -6,5 +6,5 @@
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
},
"visibility": "public",
-- "url": "https://openvibe.community/s/general/7"
++ "url": "https://openvibe.community/forum/general/7"
}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex ed2e0252..43a94859 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -64720,7 +64720,7 @@
},
"url": {
"type": "string",
-- "description": "/s/<space>/t/<slug>"
++ "description": "/forum/<space>/<slug>"
},
"kind": {
"type": "string",
@@@ -65936,7 +65936,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/community/thread-read-result.v1.json",
"title": "CommunityThreadReadResult",
-- "description": "community.thread-read-result@1: answers of community.thread.read on OpenVibe.Community. GET /api/v1/spaces/:space/threads?sort=&page=&limit=&category=&status= → a page of threads with the space, paging, categories, children and what the viewer may do; GET /api/v1/spaces/:space/threads/:slug?page= → the thread with a page of posts (reactions, author stats, crosspost info, attachments settings, paging, viewer). GET /s/:space and /s/:space/t/:slug are the same as HTML pages; GET /s/feed.xml and /s/:space/feed.xml are RSS feeds of public threads.",
++ "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 /forum/:space and /forum/:space/:slug are the same as HTML pages; GET /forum/feed.xml and /forum/:space/feed.xml are RSS feeds of public threads.",
"anyOf": [
{
"type": "object",
@@@ -66057,12 +66057,12 @@
{
"type": "string",
"contentMediaType": "text/html",
-- "description": "GET /s/:space and /s/:space/t/:slug: HTML pages."
++ "description": "GET /forum/:space and /forum/:space/:slug: HTML pages."
},
{
"type": "string",
"contentMediaType": "application/rss+xml",
-- "description": "GET /s/feed.xml and /s/:space/feed.xml: RSS feeds."
++ "description": "GET /forum/feed.xml and /forum/:space/feed.xml: RSS feeds."
}
]
}
ddiff --git a/generated/openapi/community.json b/generated/openapi/community.json
iindex ed751fc2..64eb5d4a 100644
---- a/generated/openapi/community.json
++++ b/generated/openapi/community.json
@@@ -2154,16 +2154,16 @@
}
}
},
-- "/pulse": {
++ "/forum/{space}": {
"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.",
++ "operationId": "get_forum_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.pulse"
++ "community.thread"
],
"x-openvibe-capabilities": [
-- "community.pulse.read"
++ "community.thread.read"
],
"x-openvibe-visibility": [
"public"
@@@ -2172,17 +2172,27 @@
"security": [
{
"openvibeToken": [
-- "community.pulse.read"
++ "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.pulse-read-result.v1"
++ "$ref": "#/components/schemas/community.thread-read-result.v1"
}
}
}
@@@ -2200,16 +2210,16 @@
}
}
},
-- "/s": {
++ "/forum/{space}/{slug}": {
"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.",
++ "operationId": "get_forum_space_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.space"
++ "community.thread"
],
"x-openvibe-capabilities": [
-- "community.space.read"
++ "community.thread.read"
],
"x-openvibe-visibility": [
"public"
@@@ -2218,17 +2228,35 @@
"security": [
{
"openvibeToken": [
-- "community.space.read"
++ "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.space-read-result.v1"
++ "$ref": "#/components/schemas/community.thread-read-result.v1"
}
}
}
@@@ -2246,9 +2274,9 @@
}
}
},
-- "/s/{space}": {
++ "/forum/{space}/feed.xml": {
"get": {
-- "operationId": "get_s_space",
++ "operationId": "get_forum_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": [
@@@ -2302,9 +2330,9 @@
}
}
},
-- "/s/{space}/feed.xml": {
++ "/forum/feed.xml": {
"get": {
-- "operationId": "get_s_space_feed_xml",
++ "operationId": "get_forum_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": [
@@@ -2324,16 +2352,6 @@
]
}
],
-- "parameters": [
-- {
-- "name": "space",
-- "in": "path",
-- "required": true,
-- "schema": {
-- "type": "string"
-- }
-- }
-- ],
"responses": {
"2XX": {
"description": "Success",
@@@ -2358,16 +2376,16 @@
}
}
},
-- "/s/{space}/t/{slug}": {
++ "/pulse": {
"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.",
++ "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.thread"
++ "community.pulse"
],
"x-openvibe-capabilities": [
-- "community.thread.read"
++ "community.pulse.read"
],
"x-openvibe-visibility": [
"public"
@@@ -2376,35 +2394,17 @@
"security": [
{
"openvibeToken": [
-- "community.thread.read"
++ "community.pulse.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"
++ "$ref": "#/components/schemas/community.pulse-read-result.v1"
}
}
}
@@@ -2422,16 +2422,16 @@
}
}
},
-- "/s/feed.xml": {
++ "/s": {
"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.",
++ "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.thread"
++ "community.space"
],
"x-openvibe-capabilities": [
-- "community.thread.read"
++ "community.space.read"
],
"x-openvibe-visibility": [
"public"
@@@ -2440,7 +2440,7 @@
"security": [
{
"openvibeToken": [
-- "community.thread.read"
++ "community.space.read"
]
}
],
@@@ -2450,7 +2450,7 @@
"content": {
"application/json": {
"schema": {
-- "$ref": "#/components/schemas/community.thread-read-result.v1"
++ "$ref": "#/components/schemas/community.space-read-result.v1"
}
}
}
@@@ -3559,7 +3559,7 @@
},
"url": {
"type": "string",
-- "description": "/s/<space>/t/<slug>"
++ "description": "/forum/<space>/<slug>"
},
"kind": {
"type": "string",
@@@ -5265,7 +5265,7 @@
},
"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.",
++ "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 /forum/:space and /forum/:space/:slug are the same as HTML pages; GET /forum/feed.xml and /forum/:space/feed.xml are RSS feeds of public threads.",
"anyOf": [
{
"type": "object",
@@@ -5386,12 +5386,12 @@
{
"type": "string",
"contentMediaType": "text/html",
-- "description": "GET /s/:space and /s/:space/t/:slug: HTML pages."
++ "description": "GET /forum/:space and /forum/:space/:slug: HTML pages."
},
{
"type": "string",
"contentMediaType": "application/rss+xml",
-- "description": "GET /s/feed.xml and /s/:space/feed.xml: RSS feeds."
++ "description": "GET /forum/feed.xml and /forum/:space/feed.xml: RSS feeds."
}
]
},
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex 3ae7deec..32b2d281 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -206745,7 +206745,7 @@ export interface CommunityForumThread {
slug: string;
title: string;
/**
-- * /s/<space>/t/<slug>
++ * /forum/<space>/<slug>
*/
url: string;
/**
@@@ -207060,7 +207060,7 @@ export type CommunitySpaceReadResult =
/** [email protected] (owner: community) */
/**
-- * community.thread-read-result@1: answers of community.thread.read on OpenVibe.Community. GET /api/v1/spaces/:space/threads?sort=&page=&limit=&category=&status= → a page of threads with the space, paging, categories, children and what the viewer may do; GET /api/v1/spaces/:space/threads/:slug?page= → the thread with a page of posts (reactions, author stats, crosspost info, attachments settings, paging, viewer). GET /s/:space and /s/:space/t/:slug are the same as HTML pages; GET /s/feed.xml and /s/:space/feed.xml are RSS feeds of public threads.
++ * 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 /forum/:space and /forum/:space/:slug are the same as HTML pages; GET /forum/feed.xml and /forum/:space/feed.xml are RSS feeds of public threads.
*/
export type CommunityThreadReadResult =
| {
ddiff --git a/manifests/capabilities/community.thread.read.json b/manifests/capabilities/community.thread.read.json
iindex f5d9bab4..ab3f8bc7 100644
---- a/manifests/capabilities/community.thread.read.json
++++ b/manifests/capabilities/community.thread.read.json
@@@ -17,10 +17,10 @@
"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"
++ "GET /forum/:space",
++ "GET /forum/:space/:slug",
++ "GET /forum/feed.xml",
++ "GET /forum/: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 c4ded914..e2cdd44b 100644
---- a/manifests/capabilities/community.vote.set.json
++++ b/manifests/capabilities/community.vote.set.json
@@@ -17,6 +17,6 @@
"implementedBy": [
"POST /api/v1/spaces/:space/threads/:slug/votes",
"POST /api/v1/comments/:commentId/votes",
-- "POST /s/:space/t/:slug/vote"
++ "POST /forum/:space/:slug/vote"
]
}