openvibers/openvibe.network Commits

Revoke Chat's live.chat_mirror.write: the read mirror to Live is retired (#64)

OP OpenVibers committed 5 Oct 2026 · 6925b8a1ca · 2 files, +33 −4

The Live read mirror is retired: Chat #25 (deployed) removed the sender and its capture triggers, and Live is removing the receiver. Chat no longer needs live.chat_mirror.write on openvibe.live, so it leaves DEFAULT_GRANTS and joins REVOKED_GRANTS (revoked at every boot, like Live's old coins transfer grant). test/retired-grants.test.js: an old database's grant is revoked at boot, Chat keeps live.chat_effects.write, no default hands the mirror grant out. ov test test/retired-grants.test.js test/loyalty-policy.test.js test/principals.test.js test/grants-admin.test.js: all pass.

Built in the main session (Opus).

Changes

ddiff --git a/server/identity/principals.js b/server/identity/principals.js
iindex cb43ed24..911309c8 100644
---- a/server/identity/principals.js
++++ b/server/identity/principals.js
@@@ -236,11 +236,10 @@ const DEFAULT_GRANTS = [
['live', 'community.comment.write', 'openvibe.community', []],
['live', 'community.comment.moderate', 'openvibe.community', []],
['live', 'community.pulse.write', 'openvibe.community', []],
-- // Wave 6: OpenVibe.Chat reads Live's chat context, asks Live for effects and mirrors chat rows
-- // back; Live bridges its remaining chat writers to Chat and reads presence.
++ // Wave 6: OpenVibe.Chat reads Live's chat context and asks Live for effects; Live bridges its remaining
++ // chat writers to Chat and reads presence. (The read mirror back to Live was retired 2026-10-05: see REVOKED_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', []],
@@@ -285,7 +284,11 @@ const DEFAULT_GRANTS = [
];
// Grants withdrawn by decision; applied at every boot so an old default can't come back.
--const REVOKED_GRANTS = [['live', 'network.coins.transfer', SELF_AUDIENCE]];
++const REVOKED_GRANTS = [
++ ['live', 'network.coins.transfer', SELF_AUDIENCE],
++ // The Live read mirror is retired (2026-10-05): Chat #25 removed the sender, Live the receiver.
++ ['chat', 'live.chat_mirror.write', 'openvibe.live'],
++];
// Default grants whose namespaces changed: a row still exactly as the old default seeded it is moved to
// the new list at boot, in this order (a row someone edited is left alone). Live's write grant lost
ddiff --git a/test/retired-grants.test.js b/test/retired-grants.test.js
nnew file mode 100644
iindex 00000000..b4d13d2d
---- /dev/null
++++ b/test/retired-grants.test.js
@@@ -0,0 +1,26 @@
++'use strict';
++// Retired grants stay retired: the Live read mirror was removed on both sides (Chat #25, Live), so Chat's
++// live.chat_mirror.write grant is revoked at every boot and no default hands it out again.
++const assert = require('assert');
++const { getDb } = require('../server/db/database');
++
++(async () => {
++const log = console.log; console.log = () => {};
++const db = await getDb();
++console.log = log;
++const principals = require('../server/identity/principals');
++await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('chat', 'x', 'chat', '[]', 1) ON CONFLICT DO NOTHING").run();
++const grant = async (cap) => (await principals.grantsFor(db, 'chat', 'openvibe.live')).find((g) => g.capability === cap);
++
++// A database as an older release left it: Chat still holds the mirror grant.
++await principals.ensureSchema(db);
++await db.prepare("INSERT INTO principal_grants (client_id, capability, audience, namespaces, granted_by) VALUES ('chat', 'live.chat_mirror.write', 'openvibe.live', '[]', 'default') ON CONFLICT (client_id, capability, audience) DO UPDATE SET revoked_at = NULL").run();
++assert.ok(await grant('live.chat_mirror.write'), 'the old mirror grant is there before the boot');
++
++await principals.ensureSchema(db); // the next boot
++assert.strictEqual(await grant('live.chat_mirror.write'), undefined, 'the mirror grant is revoked at boot');
++assert.ok(await grant('live.chat_effects.write'), 'Chat keeps its effects grant');
++assert.ok(!principals.DEFAULT_GRANTS.some(([c, cap]) => c === 'chat' && cap === 'live.chat_mirror.write'), 'no default hands it out');
++
++console.log('retired grants: all checks passed');
++})().catch(err => { console.error(err); process.exit(1); });