openvibers/openvibe.network Commits

Grants for plan T3 and T4, on Contracts 0.80.0: Live reads a channel's moderation settings, moderators and emote count from Chat (chat.moderation.read); Chat uploads and deletes emote images in Media's chat namespace and runs the chat-AI summaries with its own token (ai.run.create/read, chat.*); Live reads, lists, uploads and deletes its objects in Media's live namespace and OpenRe uploads there, so both drop Media API keys. principals.test.js pins Live's Media scope. npm test green.

OP OpenVibers committed 29 Sept 2026 · b5d870737f · 4 files, +21 −5


Changes

ddiff --git a/package-lock.json b/package-lock.json
iindex 12765ab3..50211780 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -20,7 +20,7 @@
"helmet": "^8.1.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",
++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.80.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.12.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.29.2",
"uuid": "^11.1.0",
@@@ -1465,9 +1465,9 @@
}
},
"node_modules/openvibe-contracts": {
-- "version": "0.79.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",
-- "integrity": "sha512-FY5GLbiOq0kDw5lUPPzX6h3ATbzvsr/mX5DUCF4GRfn6vGMP0p89rZZDhgtVDaS+yCBCYMZ8zFaZxuHEkEVwVg==",
++ "version": "0.80.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.80.0",
++ "integrity": "sha512-7RCNFKxFRey3PquBwyQkSu8Bu2q2Ek0yb248SJWaymcg/g9PrlStFVUDTLMgh8i7BZSaZSngj0nojpwYTLGYDQ==",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
ddiff --git a/package.json b/package.json
iindex 7137878d..e7a6d73f 100644
---- a/package.json
++++ b/package.json
@@@ -23,7 +23,7 @@
"helmet": "^8.1.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",
++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.80.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.12.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.29.2",
"uuid": "^11.1.0",
ddiff --git a/server/identity/principals.js b/server/identity/principals.js
iindex a0f40de9..ad1f70be 100644
---- a/server/identity/principals.js
++++ b/server/identity/principals.js
@@@ -49,6 +49,17 @@ const DEFAULT_GRANTS = [
['tools', 'media.object.upload', 'openvibe.media', ['tools', 'tools.*']],
['tools', 'media.object.read', 'openvibe.media', ['tools', 'tools.*']],
['games', 'media.object.upload', 'openvibe.media', ['games']],
++ // Plan T4 (Media cleanup): Live and OpenRe reach Media with their own tokens instead of API keys. Live keeps
++ // its objects under the live namespace (VOD/clip/thumbnail uploads, reads, lists, deletes); OpenRe uploads the
++ // recordings and thumbnails it produces there too.
++ ...['media.object.read', 'media.object.list', 'media.object.upload', 'media.object.delete'].map(cap => ['live', cap, 'openvibe.media', ['live']]),
++ ['openre', 'media.object.upload', 'openvibe.media', ['live']],
++ // Plan T3: Chat owns the six chat tables. Emote images go to Media under the chat namespace, and
++ // the chat-AI summaries run in Chat with its own token.
++ ['chat', 'media.object.upload', 'openvibe.media', ['chat']],
++ ['chat', 'media.object.delete', 'openvibe.media', ['chat']],
++ ['chat', 'ai.run.create', 'openvibe.ai', ['chat.*']],
++ ['chat', 'ai.run.read', 'openvibe.ai', ['chat.*']],
['games', 'identity.subject.resolve', SELF_AUDIENCE, []],
// Games subscribes to network.user.token_valid_after (sign-out everywhere closes game sessions).
['games', 'events.subscription.manage', 'openvibe.events', []],
@@@ -214,6 +225,8 @@ const DEFAULT_GRANTS = [
['community', 'network.blocks.read', SELF_AUDIENCE, []],
['live', 'chat.live_bridge.write', 'openvibe.chat', []],
['live', 'chat.presence.read', 'openvibe.chat', []],
++ // Plan T3: Live reads a channel's moderation settings, its moderators and emote count from Chat.
++ ['live', 'chat.moderation.read', 'openvibe.chat', []],
['live', 'chat.message.send', 'openvibe.chat', []],
// Wave 8: Live as a Billing client (used only with BILLING_AUTHORITY=billing). Never cashout.manage
// or ledger.admin: approving payouts is a separately controlled capability (ADR-012 rule 10).
ddiff --git a/test/principals.test.js b/test/principals.test.js
iindex e3c11464..1e328e0a 100644
---- a/test/principals.test.js
++++ b/test/principals.test.js
@@@ -93,6 +93,9 @@ const server = http.createServer(app);
assert.strictEqual(t.body.scope, 'identity.subject.resolve network.account.deletion.confirm network.account.export.contribute', 'Media resolves object owners to subjects and takes part in account export and deletion (ADR-033), nothing else here');
const mediaResolve = t.body.access_token;
t = await token({ client_id: 'live', client_secret: 'live-secret', audience: 'openvibe.media' });
++ assert.strictEqual(t.status, 200, JSON.stringify(t.body));
++ assert.deepStrictEqual(t.body.scope.split(' ').sort(), ['media.object.delete', 'media.object.list', 'media.object.read', 'media.object.upload'], 'plan T4: Live reaches Media with its own token, nothing more');
++ t = await token({ client_id: 'live', client_secret: 'live-secret', audience: 'openvibe.games' });
assert.strictEqual(t.status, 400, 'no grants for that audience');
// ── Guarded routes ──