openvibers/openvibe.network Commits

Chat may manage its own OpenVibe.Events subscriptions: default grant chat events.subscription.manage on openvibe.events (seeded at boot like the others). Chat subscribes to live.release.deployed (the deploy card; register C-84, so the Live bridge's deployNotice can go after one clean deploy) and network.module.updated (its chat.preferences cache drops a changed record at once instead of within 60 s). Events checks only this capability for a service subscription; it has no per-topic read grants. test/module-events.test.js: the chat client gets a token for audience openvibe.events with that scope

OP OpenVibers committed 23 Sept 2026 · 09bf748b7f · 2 files, +6 −0


Changes

ddiff --git a/server/identity/principals.js b/server/identity/principals.js
iindex 9f4a3e1a..1ef636c7 100644
---- a/server/identity/principals.js
++++ b/server/identity/principals.js
@@@ -137,6 +137,9 @@ const DEFAULT_GRANTS = [
['chat', 'live.chat_context.read', 'openvibe.live', []],
['chat', 'live.chat_effects.write', 'openvibe.live', []],
['chat', 'live.chat_mirror.write', 'openvibe.live', []],
++ // Chat consumes live.release.deployed (the deploy card, register C-84) and network.module.updated
++ // (its chat.preferences cache) through its own Events subscriptions, created at Chat's boot.
++ ['chat', 'events.subscription.manage', 'openvibe.events', []],
['live', 'chat.live_bridge.write', 'openvibe.chat', []],
['live', 'chat.presence.read', 'openvibe.chat', []],
['live', 'chat.message.send', 'openvibe.chat', []],
ddiff --git a/test/module-events.test.js b/test/module-events.test.js
iindex 85cfeaa2..810b9922 100644
---- a/test/module-events.test.js
++++ b/test/module-events.test.js
@@@ -71,6 +71,9 @@ const valid = (e) => { const v = contracts.validate('events.event-envelope@1', e
assert.deepStrictEqual(grant('chat', 'network.modules.write'), ['chat.preferences']);
assert.deepStrictEqual(grant('live', 'network.modules.write'), ['chat.tts_defaults', 'live.profile'], 'the old default narrows at boot');
assert.ok(grant('live', 'network.modules.read').includes('chat.preferences'), 'Live keeps reading it');
++ // Chat manages its own Events subscriptions (live.release.deployed, network.module.updated).
++ const chatEvents = await fetch(`${base}/oauth/token`, { method: 'POST', body: new URLSearchParams({ grant_type: 'client_credentials', client_id: 'chat', client_secret: 'chat-secret', audience: 'openvibe.events', scope: 'events.subscription.manage' }) }).then(r => r.json());
++ assert.strictEqual(chatEvents.scope, 'events.subscription.manage', JSON.stringify(chatEvents));
const chatTok = await svc('chat');
const claims = JSON.parse(Buffer.from(chatTok.access_token.split('.')[1], 'base64url').toString());
assert.deepStrictEqual(claims.ns, ['chat.preferences']);