openvibers/openvibe.services Commits

Services: Community is the tenth authority (pastes and spaces in Your resources) (#39)

OP OpenVibers committed 10 Oct 2026 · 0a09d0a00a · 6 files, +20 −19

* openvibe-contracts v0.134.0 (was v0.131.0)

Opened by scripts/pin-bump.js in OpenVibers/OpenVibe.Shared after the v0.134.0 release.

* Services: Community is the tenth authority (pastes and spaces in Your resources)

Plan T13: OpenVibe.Community is the tenth authority in the merged index, so "Your resources" shows a person's pastes and the forum spaces they created. This PR carries the openvibe-contracts v0.134.0 pin bump (#38, folded in), which declares `community.resource.read` active with Community's loopback `internalOrigin`.

- **No registry code changes.** The registry is built from the pinned manifests: every service whose manifest lists an active `<id>.resource.read`. The new pin is enough.
- **`test/authorities.test.js`:** pinned to 0.134.0. It expects eleven active `<id>.resource.read` (ten authorities, Community among them) and Community's origin `http://127.0.0.1:4200` and audience `openvibe.community`.
- **Your resources** (`/projects/mine`) and the projects list name pastes and forum spaces among what appears there.

Community serves the index (Community#59) and Network grants the `services` principal (Network#137); both ship before this.

`ov test`: green (authorities and project-resources included).

Changes

ddiff --git a/README.md b/README.md
iindex 38d1d83..afc54a2 100644
---- a/README.md
++++ b/README.md
@@@ -102,7 +102,7 @@ There is no polling loop: the index reads the authorities on each request (step
- **OpenVibe.Events** — the outbox relay publishes `services.app.*` with Services' service token (`events.event.publish`); the Events playground calls it with the app's token (`events.app.publish`); the project archive pulls the project's app events with a Network export token (`events.app.read`).
- **OpenVibe.Media** — the Media playground uploads with the app's token into the project's namespace; the project archive lists the project's objects, namespaces and download URLs with a Network export token (`media.object.list`, `media.object.read`).
- **PostgreSQL 18 and Valkey 9** (OpenVibe.Host `roles/data/`, ADR-035): every read and write is async through `openvibe-sdk/db`; Valkey holds the per-actor limit counters (optional).
--- **openvibe-contracts v0.131.0**, **openvibe-sdk v0.35.1**, **openvibe-shared v3.0.1** (pinned tag tarballs; the docs show the tag and the package version, and say so when they differ).
++- **openvibe-contracts v0.134.0**, **openvibe-sdk v0.35.1**, **openvibe-shared v3.0.1** (pinned tag tarballs; the docs show the tag and the package version, and say so when they differ).
No path in Services sends or accepts a shared loopback key (tested by grep and at runtime).
@@@ -144,7 +144,7 @@ one receipt per export and deletion in `account_data_events` (migration 0003).
- The resource index's owner becomes NULL.
- The append-only release log, the control log and the staff trust tiers stay attributed, for accountability.
--The Services service manifest, `services.release.manage|read`, `services.resource.read` and `services.app-manifest@1` are released in openvibe-contracts v0.131.0 (they replaced the `codes.*` forms, retired that release); the CI contracts check is blocking.
++The Services service manifest, `services.release.manage|read`, `services.resource.read` and `services.app-manifest@1` are released in openvibe-contracts v0.134.0 (they replaced the `codes.*` forms, retired that release); the CI contracts check is blocking.
For the playgrounds to succeed end to end (not Services' code; configuration elsewhere): Network `DEV_SANDBOX_AUDIENCES` including `openvibe.media` and `openvibe.events`, a staff-set allowance containing `media.object.upload` and `events.app.publish` for the project, a Media tenant keyed by the project id, and OpenVibe.Events serving `events.app.publish` for app tokens. On 2026-09-23 these were in place on production: a sandbox app uploaded to Media (tenant `prj_…-sandbox`) and published and read app events through public endpoints. That run used curl, not the playgrounds, and it is not yet a committed, repeatable check.
@@@ -251,7 +251,7 @@ Reporting a vulnerability: [SECURITY.md](SECURITY.md).
Part of the [OpenVibe network](https://openvibe.network). Built in the open by [OpenVibers](https://github.com/OpenVibers).
<!-- versions:start -->
--- openvibe-contracts: v0.131.0
++- openvibe-contracts: v0.134.0
- openvibe-sdk: v0.42.0
- openvibe-shared: v3.0.1
<!-- versions:end -->
ddiff --git a/STATUS.json b/STATUS.json
iindex 9c19505..75a6471 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -7,11 +7,11 @@
"runtime": "node 22, express 4, PostgreSQL 18 (openvibe-sdk/db)",
"port": 4930,
"domain": "openvibe.services",
-- "contracts": "openvibe-contracts v0.131.0",
++ "contracts": "openvibe-contracts v0.134.0",
"sdk": "openvibe-sdk v0.42.0",
"shared": "openvibe-shared v3.0.1",
"packages": [
-- "openvibe-contracts v0.131.0",
++ "openvibe-contracts v0.134.0",
"openvibe-sdk v0.42.0",
"openvibe-shared v3.0.1"
],
ddiff --git a/package-lock.json b/package-lock.json
iindex e43beea..d296294 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -16,7 +16,7 @@
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"iovalkey": "^0.4.0",
-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.131.0",
++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.134.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.42.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v3.0.1",
"pg": "^8.23.0"
@@@ -890,9 +890,9 @@
}
},
"node_modules/openvibe-contracts": {
-- "version": "0.131.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.131.0",
-- "integrity": "sha512-ZojAzdJCkPnUPvZcS583p7roPmfaG4u8RL1r9yFrn+77JO9yg7Nmz2gvjQoTtX2AzWR7tI1bCtFd9MhprUioCg==",
++ "version": "0.134.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.134.0",
++ "integrity": "sha512-sWjPIaJtv/V1evsLbNcXecsiHVHd7w+A7/VD7o41j4Veotd+zfQRnNHFHS2gFlOVGR3WBycEzOZzo7+r1jk3sg==",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
ddiff --git a/package.json b/package.json
iindex 2ab6902..822aaca 100644
---- a/package.json
++++ b/package.json
@@@ -27,7 +27,7 @@
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"iovalkey": "^0.4.0",
-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.131.0",
++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.134.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.42.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v3.0.1",
"pg": "^8.23.0"
ddiff --git a/server/http/portal.js b/server/http/portal.js
iindex cb2b6d2..f292c7d 100644
---- a/server/http/portal.js
++++ b/server/http/portal.js
@@@ -123,7 +123,7 @@ ${table(['Project', 'Your role', 'Environments', 'Apps', 'Created'], list.map((p
html`<a href="/projects/${p.id}">${p.name}</a><br><code class="small">${p.id}</code>${p.archived_at ? html` ${badge('archived', 'bad')}` : ''}`,
p.role || (req.viewer.staff ? 'staff' : '—'), (p.environments || []).join(', '), p.counts ? p.counts.apps : '—', time(p.created_at),
]), { empty: 'You are not a member of any project yet.' })}
--<p><a href="/projects/mine">Your resources</a> <span class="muted small">what you own outside any project: robots, chat rooms, streams, watches, Actor tasks</span></p>
++<p><a href="/projects/mine">Your resources</a> <span class="muted small">what you own outside any project: robots, chat rooms, streams, watches, Actor tasks, pastes</span></p>
<h2>Create a project</h2>
<form method="post" action="/projects" class="inline-form">${csrfField(csrf(req))}
<label>Name <input name="name" required maxlength="80"></label><button type="submit">Create</button></form>
@@@ -150,8 +150,8 @@ ${table(['Project', 'Your role', 'Environments', 'Apps', 'Created'], list.map((p
scope: { owner: req.viewer.subject }, base: '/projects/mine',
title: 'Your resources', crumbs: [{ label: 'Projects', href: '/projects' }, { label: 'Your resources' }],
heading: html`<h1>Your resources</h1>`,
-- intro: 'What you own yourself, outside any project: your robots, chat rooms, streams, watches, Actor tasks and the rest. Each OpenVibe service answers for its own, read just now and merged here; the service that holds each one owns it.',
-- empty: 'Nothing of yours outside a project yet. A robot you add on OpenVibe.Bot, a room you open on OpenVibe.Chat, a stream on OpenRestream or a watch on OpenVibe.Watch appears here.',
++ intro: 'What you own yourself, outside any project: your robots, chat rooms, streams, watches, Actor tasks, pastes, forum spaces and the rest. Each OpenVibe service answers for its own, read just now and merged here; the service that holds each one owns it.',
++ empty: 'Nothing of yours outside a project yet. A robot you add on OpenVibe.Bot, a room you open on OpenVibe.Chat, a stream on OpenRestream, a watch on OpenVibe.Watch or a paste on OpenVibe.Community appears here.',
footer: html`Resources in a project are on that project's page: <a href="/projects">your projects</a>.`,
ownerColumn: false,
});
ddiff --git a/test/authorities.test.js b/test/authorities.test.js
iindex 4a0a1d0..ee917fd 100644
---- a/test/authorities.test.js
++++ b/test/authorities.test.js
@@@ -1,9 +1,9 @@
'use strict';
/**
-- * server/authorities/index.js against the REAL pinned openvibe-contracts (v0.131.0): the registry is
++ * server/authorities/index.js against the REAL pinned openvibe-contracts (v0.134.0): the registry is
* derived from the released service manifests — every non-placeholder service whose manifest lists an
-- * active <id>.resource.read — and never hand-maintained. The nine authorities of the pin (0.130.0 added
-- * watch, actor, bot and chat, 0.131.0 OpenRestream; plan T13 step 8 phase 2), their manifest
++ * active <id>.resource.read — and never hand-maintained. The ten authorities of the pin (0.130.0 added
++ * watch, actor, bot and chat, 0.131.0 OpenRestream, 0.134.0 Community; plan T13 step 8 phase 2), their manifest
* loopback origins and their audiences; Services itself (read in process: server/registry/self-authority.js),
* Codes (retired codes.resource.read at 0.113.0, when its portal moved here), placeholders and services without
* the capability are not authorities.
@@@ -17,12 +17,12 @@ const { createAuthorities } = require('../server/authorities');
const { check, done } = require('./helpers/app');
const quiet = { warn() {}, log() {}, error() {} };
--const AUTHORITIES = ['actor', 'bot', 'chat', 'events', 'host', 'media', 'network', 'openre', 'watch'];
++const AUTHORITIES = ['actor', 'bot', 'chat', 'community', 'events', 'host', 'media', 'network', 'openre', 'watch'];
async function main() {
-- await check('the pin lists ten active <id>.resource.read capabilities, nine of them authorities', () => {
++ await check('the pin lists eleven active <id>.resource.read capabilities, ten of them authorities', () => {
const ids = contracts.capabilities.manifests.filter((c) => /^([a-z][a-z0-9-]{0,31})\.resource\.read$/.test(c.id) && c.status === 'active').map((c) => c.id).sort();
-- assert.deepStrictEqual(ids, ['actor.resource.read', 'bot.resource.read', 'chat.resource.read', 'events.resource.read', 'host.resource.read',
++ assert.deepStrictEqual(ids, ['actor.resource.read', 'bot.resource.read', 'chat.resource.read', 'community.resource.read', 'events.resource.read', 'host.resource.read',
'media.resource.read', 'network.resource.read', 'openre.resource.read', 'services.resource.read', 'watch.resource.read']);
assert.strictEqual(contracts.capabilities.get('codes.resource.read').status, 'retired', 'Codes owns no resources since 0.113.0');
// services.resource.read is Services' own id; Services is never its own authority (the next check).
@@@ -42,6 +42,7 @@ async function main() {
bot: 'http://127.0.0.1:4630',
chat: 'http://127.0.0.1:4400',
openre: 'http://127.0.0.1:4500',
++ community: 'http://127.0.0.1:4200',
};
for (const [id, origin] of Object.entries(expected)) {
const a = registry.get(id);