openvibers/openvibe.network Commits

Tools grants: live, chat and community hold tools.tool.run and tools.job.read on openvibe.tools, so their Tools run API calls (kiosk page titles, audio conversion, save-as-paste) run on the service tier, never network probes

OP OpenVibers committed 24 Sept 2026 · cbe77e1540 · 2 files, +5 −0


Changes

ddiff --git a/server/identity/principals.js b/server/identity/principals.js
iindex 40fd0f12..ed7c64e3 100644
---- a/server/identity/principals.js
++++ b/server/identity/principals.js
@@@ -155,6 +155,9 @@ const DEFAULT_GRANTS = [
// or ledger.admin: approving payouts is a separately controlled capability (ADR-012 rule 10).
...['billing.intent.create', 'billing.transfer.create', 'billing.balance.read', 'billing.cashout.request',
'billing.subscription.manage', 'billing.entitlement.check'].map(c => ['live', c, 'openvibe.billing', []]),
++ // Tools platform S9: products call the Tools run API (the kiosk's page titles, Chat's audio conversion,
++ // Community's save-as-paste) with their own token, on the service tier instead of the anonymous one.
++ ...['live', 'chat', 'community'].flatMap(c => ['tools.tool.run', 'tools.job.read'].map(cap => [c, cap, 'openvibe.tools', []])),
];
// Grants withdrawn by decision; applied at every boot so an old default can't come back.
ddiff --git a/test/principals.test.js b/test/principals.test.js
iindex b265c772..f4af78a0 100644
---- a/test/principals.test.js
++++ b/test/principals.test.js
@@@ -137,6 +137,8 @@ const server = http.createServer(app);
assert.strictEqual(media.body.scope, 'media.object.upload', 'community may upload screenshot bytes to Media');
const cm = await token({ client_id: 'live', client_secret: 'live-secret', audience: 'openvibe.community' });
assert.deepStrictEqual(cm.body.scope.split(' '), ['community.comment.moderate', 'community.comment.write', 'community.paste.create', 'community.paste.moderate', 'community.paste.write', 'community.pulse.write']);
++ const tl = await token({ client_id: 'live', client_secret: 'live-secret', audience: 'openvibe.tools' });
++ assert.deepStrictEqual(tl.body.scope.split(' '), ['tools.job.read', 'tools.tool.run'], 'Live runs Tools on the service tier, never probes');
// Go-live fan-out accepts Live's token (network.notifications.push); a narrower token is refused.
r = await post('/internal/events/stream-live', {}, { authorization: `Bearer ${full}` });