openvibers/openvibe.community Commits

README and STATUS.json are current (roadmap WS-U task 3): the README gains Purpose (the opening overview), Owns, Does not own, Depends on, Acceptance (what the test files prove), and Security (SECURITY.md, auth, private data, egress, secrets by name) sections. Capabilities lists all twelve capabilities in the manifest (space, thread, vote and Pulse reads were missing), names the v0.71.0 pin (was v0.49.0) and the grants Community uses at Network, Media, VIP and Events. Deploy says production deploys with sudo ovhost deploy community (not git pull and a restart), runs as ubuntu, and gives the rollback; the layout names openvibe-shared v1.22.0. STATUS.json is new, in the shape of Search's, updated 2026-09-28.

OP OpenVibers committed 28 Sept 2026 · 2da3501e28 · 2 files, +122 −4


Changes

ddiff --git a/README.md b/README.md
iindex ce59949..02864a3 100644
---- a/README.md
++++ b/README.md
@@@ -2,6 +2,8 @@
**https://openvibe.community — the people of OpenVibe.**
++## Purpose
++
The community hub of the OpenVibe network: community-run, open source, free speech within
the rules. It is the home of **pastes** (code, text and screenshots with a link), the
**forum** (spaces, threads and posts), the **comment threads** every other OpenVibe product
@@@ -11,6 +13,30 @@ It is a small Node/Express app (CommonJS, no framework, one SQLite database) tha
server-renders every page — crawlers and no-JS readers get the whole thing — and adds a
little progressive JavaScript for pagination, copy buttons and the upload path.
++## Owns
++
++- pastes (authority since 2026-09-22, `PASTES_AUTHORITY=community`), their versions, likes and
++ comments
++- the typed comment threads every other product embeds, the forum (spaces, threads, posts, votes,
++ categories), Pulse (the network's public activity) and the Discord relay
++- the `community.*` events, Search documents for public threads, and the `community.profile` user module
++- one SQLite database (`/var/lib/openvibe-community/community.db` in production)
++
++## Does not own
++
++- identity and SSO (OpenVibe.Network), file bytes (OpenVibe.Media keeps screenshots and attachments),
++ memberships (OpenVibe.VIP decides members-only spaces and threads), chat rooms (OpenVibe.Chat)
++- the content of the products whose threads it hosts: a product stores only the thread id
++
++## Depends on
++
++- OpenVibe.Network (SSO, JWKS, service tokens, `identity.subject.resolve`, the `community.profile` module)
++- OpenVibe.Media (screenshot and attachment uploads), OpenVibe.VIP (members-only gates), OpenVibe.Chat
++ (a space's room), OpenVibe.Events (the outbox relay and the Pulse and account subscriptions)
++- OpenVibe.Live only when `PASTES_AUTHORITY=live` (the rollback mode), and for old VOD/clip comment imports
++- `openvibe-contracts` v0.71.0, `openvibe-sdk` v0.12.0 (events outbox, per-actor limits),
++ `openvibe-shared` v1.22.0, pinned by release tarball
++
## How it fits the network
Pastes moved to Community in roadmap Wave 5. `PASTES_AUTHORITY` picks who owns them:
@@@ -518,12 +544,24 @@ Community checks service tokens against these capabilities (manifests in
| `community.comment.moderate` | active in contracts (v0.7.0) | thread visibility, comment/post/thread moderation, relay admin |
| `community.pulse.write` | active in contracts (v0.7.0) | publishing to Pulse |
| `community.post.create` | active in contracts (v0.7.0) | forum writes |
++| `community.space.read` / `.manage`, `community.thread.read`, `community.vote.set`, `community.pulse.read` | active in contracts | spaces, threads, votes and Pulse reads by services and apps |
--This repository pins `openvibe-contracts` v0.49.0, which knows every id above, so they all go
++This repository pins `openvibe-contracts` v0.71.0, which knows every id above, so they all go
through the library's `capabilities.check`. `server/identity/capabilities.js` still decides an id
the installed contracts do not know locally, with the library's own matching rule (the exact id
or a `prefix.*` grant).
++Called elsewhere, as the service principal `community` (the OAuth client `community`):
++
++| Service | Grant | Why |
++| --- | --- | --- |
++| OpenVibe.Network | `identity.subject.resolve`; `network.modules.write` on `community.profile` | author names and avatars; the profile module |
++| OpenVibe.Media | `media.object.upload` | new screenshots and forum attachments |
++| OpenVibe.VIP | `vip.resource.policy.evaluate` | members-only spaces and threads |
++| OpenVibe.Events | `events.event.publish`, `events.subscription.manage` | the outbox relay; Pulse, account and block subscriptions created at boot |
++
++A space's chat room is attached with the signed-in person's own Network token (no service grant).
++
### Per-actor limits
Every API router also limits who calls it, once the viewer is resolved and before the route does any
@@@ -616,17 +654,53 @@ reads); then every statement the previous release runs must still prepare after
(and migrated) a database the previous one created. After each deploy, record the release now in
production as the next N-1 and commit `test/fixtures/n-1/`.
++## Acceptance
++
++`npm test` runs every `test/*.test.js` with mock Live, Network and Media and a real RS256 key, no
++network. What they prove, among others: the paste API as the browser clients use it and the import
++(`pastes-api`, `import`); comment threads, the forum and its server-rendered pages (`comments`, `forum`,
++`forum-ssr`, `ssr`); members-only gates that fail closed (`members-only`); private things stay private on
++every read path and nobody acts on someone else's ids (`security-private`, `security-idor`); SSRF,
++secrets, open redirects (`security-ssrf`, `security-secrets`, `open-redirect`); Pulse and the Events
++consumer (`pulse`, `pulse-consumer`, `events`); revocation, blocks, account export and deletion
++(`revocation`, `blocks`, `account-data`); the Discord relay (`relay*`); per-actor limits
++(`actor-limits`); graceful shutdown (`shutdown`); and the previous release against this one (`n-1`).
++
++## Security
++
++Reporting a vulnerability: [SECURITY.md](SECURITY.md). The rules the code keeps:
++
++- **Auth.** Browsers use Network JWTs (Bearer or `ov_token`); services use tokens for audience
++ `openvibe.community` with the route's capability, and name the person in `X-OV-Subject`. Identity is
++ never read from bodies or queries; a token issued before a person's `token_valid_after` is refused.
++- **Private data.** Unlisted and private pastes get unguessable slugs, burn-after-read pastes are read
++ once, members-only spaces and threads fail closed when VIP is unreachable, screenshots lose their
++ EXIF/XMP metadata, and view counts use hashed visitor ids (`VIEW_HASH_SECRET`).
++- **Egress.** Community calls only its configured Network, Live, Media, VIP, Chat and Events hosts,
++ and Discord (webhooks named by variable, the gateway) when the relay is on.
++- **Secrets.** `OV_OAUTH_CLIENT_SECRET`, `COMMUNITY_EVENTS_SECRET`, `VIEW_HASH_SECRET`,
++ `DISCORD_BOT_TOKEN` and the Discord webhook variables live in `/etc/openvibe/community.env` (0600), by
++ name only; a relay mapping stores a variable's name, never the URL.
++
## Deploy
```
/opt/openvibe.community # git checkout, `npm ci --omit=dev`
/etc/openvibe/community.env # secrets (0600)
--deploy/systemd/openvibe-community.service # → /etc/systemd/system/, User=openvibe, port 4200
++deploy/systemd/openvibe-community.service # → /etc/systemd/system/, User=ubuntu, port 4200
deploy/nginx/openvibe.community.conf # → /etc/nginx/sites-available/, TLS from
# /etc/letsencrypt/live/openvibe.community/
```
--Update: `git pull && npm ci --omit=dev && systemctl restart openvibe-community`. The schema
++Production deploys with `sudo ovhost deploy community` on the host (strategy `git-checkout`: fetch,
++fast-forward `/opt/openvibe.community`, install on a lockfile change, restart, wait for `/api/ready`).
++The unit is `openvibe-community.service` on `127.0.0.1:4200`, the env file `/etc/openvibe/community.env`. After a
++deploy, record the N-1 fixtures (`npm run n-1:record`).
++Rollback: ovhost puts the previous sha back by itself when `/api/ready` does not answer 2xx after the
++restart; afterwards `sudo ovhost rollback community --to <sha>`. Nothing blocks a rollback: the schema
++code only adds tables and columns.
++
++The schema
is created idempotently at boot in every mode (comments, the forum, Pulse and the relay live
in Community's database whichever service owns pastes; the three seed spaces are inserted
once). The database lives in the unit's `StateDirectory` (`/var/lib/openvibe-community`), so
@@@ -685,7 +759,7 @@ server/
render/highlight.js highlight.js wrapper, language list, download extensions
seo.js robots, sitemap, RSS, JSON-LD builders
public/ css/community.css, js/community.js, favicon.svg, og-default.png
--(openvibe-shared is the pinned OpenVibe.Shared v1.5.1 release, installed by npm)
++(openvibe-shared is the pinned OpenVibe.Shared v1.22.0 release, installed by npm)
deploy/ systemd unit, nginx vhost
scripts/import-pastes.js Media paste bundle importer
scripts/import-live-comments.js Live's VOD/clip comments → Community threads (dry run by default)
ddiff --git a/STATUS.json b/STATUS.json
nnew file mode 100644
iindex 0000000..ee18dfb
---- /dev/null
++++ b/STATUS.json
@@@ -0,0 +1,44 @@
++{
++ "repository": "OpenVibers/OpenVibe.Community",
++ "stage": "alpha",
++ "code": true,
++ "wave": "W5",
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "runtime": "node 22, express, better-sqlite3",
++ "port": 4200,
++ "domain": "openvibe.community",
++ "contracts": "openvibe-contracts v0.71.0",
++ "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"
++ ],
++ "capabilitiesRegistered": true,
++ "pastesAuthority": "community in production since 2026-09-22 (~23:35 UTC); live is the rollback mode",
++ "deployed": true,
++ "deployment": "Public at https://openvibe.community: deployed with `sudo ovhost deploy community`, /opt/openvibe.community, unit openvibe-community on 127.0.0.1:4200 behind the openvibe.community nginx vhost, principal community. The paste import brought 889 + 2 pastes, 60 comments and 48 likes (5 rows held). The Discord relay is off. Restore drill passed 2026-09-23.",
++ "features": [
++ "pastes: code, text and screenshots, with versions, likes, comments, forks and burn-after-read",
++ "typed comment threads embedded by other products",
++ "forum: spaces, threads, posts, categories and votes",
++ "members-only spaces and threads gated by OpenVibe.VIP",
++ "a space's chat room on OpenVibe.Chat",
++ "Pulse: the network's public activity from Events",
++ "Discord relay (off in production)",
++ "server-rendered pages with full SEO",
++ "Search documents and community.* events",
++ "revocation, blocks, account merge, export and deletion from Network events",
++ "N-1 compatibility tests",
++ "per-actor limits (openvibe-sdk/limits)"
++ ],
++ "updated": "2026-09-28"
++}