openvibers/openvibe.network Commits

Grants: services reads the watch, actor, bot and chat resource indexes (#129)

OP OpenVibers committed 10 Oct 2026 · b497b2ab0d · 2 files, +39 −1

Plan T13 step 8 phase 2: the `services` principal is granted `watch.resource.read`, `actor.resource.read`, `bot.resource.read` and `chat.resource.read`, each for that service's audience. OpenVibe.Services can then read the four new indexes (Watch#28, Actor#22, Bot#68, Chat#57, deployed), which become active in Contracts 0.130.0.

New `test/services-resource-grants.test.js`: every `<id>.resource.read` the pinned contracts make active must be granted to `services` with the right audience (Network's own for network). Without that, Services shows the authority as partial forever. Granted ones must exist and not also be revoked.

Changes

ddiff --git a/server/identity/principals.js b/server/identity/principals.js
iindex 7645fe9d..202306c1 100644
---- a/server/identity/principals.js
++++ b/server/identity/principals.js
@@@ -213,7 +213,8 @@ const DEFAULT_GRANTS = [
// Plan T13: OpenVibe.Services merges every authority's resource index (ADR-048 section 3): it reads each one's
// GET /api/v1/resources under that authority's <id>.resource.read, with a token for that authority's audience.
['services', 'network.resource.read', SELF_AUDIENCE, []],
-- ...['media', 'events', 'host'].map(id => ['services', `${id}.resource.read`, `openvibe.${id}`, []]),
++ // Phase 2 (contracts 0.129.0/0.130.0): watches, Actor tasks, robots and rooms.
++ ...['media', 'events', 'host', 'watch', 'actor', 'bot', 'chat'].map(id => ['services', `${id}.resource.read`, `openvibe.${id}`, []]),
// Plan T14: OpenVibe.Run publishes run.job.* and hands each job to a paired Node through Bot's internal jobs API
// (POST /api/v1/jobs, /jobs/:id/cancel, GET /jobs/:id, capability bot.job.dispatch, service-to-service only).
['run', 'events.event.publish', 'openvibe.events', []],
ddiff --git a/test/services-resource-grants.test.js b/test/services-resource-grants.test.js
nnew file mode 100644
iindex 00000000..be32857d
---- /dev/null
++++ b/test/services-resource-grants.test.js
@@@ -0,0 +1,37 @@
++/**
++ * OpenVibe.Services merges every authority's resource index (ADR-048, plan T13 step 8): it reads each one's
++ * GET /api/v1/resources under that authority's `<id>.resource.read`, with a token for `openvibe.<id>`. Every
++ * capability of that form that the pinned contracts release has made active must therefore be granted to the
++ * `services` principal here, or Services lists that authority as partial forever. Planned ones may be granted
++ * ahead (their indexes ship before the capability turns active).
++ *
++ * node test/services-resource-grants.test.js
++ */
++'use strict';
++const assert = require('assert');
++const contracts = require('openvibe-contracts');
++const principals = require('../server/identity/principals');
++
++const RESOURCE_READ = /^([a-z][a-z0-9-]{0,31})\.resource\.read$/;
++const granted = new Map(principals.DEFAULT_GRANTS.filter((g) => g[0] === 'services' && RESOURCE_READ.test(g[1])).map((g) => [g[1], g[2]]));
++const revoked = new Set((principals.REVOKED_GRANTS || []).filter((g) => g[0] === 'services').map((g) => g[1]));
++
++let checked = 0;
++for (const m of contracts.services.manifests) {
++ if (!m || m.id === 'services' || m.status === 'retired' || m.status === 'placeholder') continue;
++ const id = `${m.id}.resource.read`;
++ const cap = contracts.capabilities.get(id);
++ if (!cap || !(m.capabilities || []).includes(id)) continue;
++ if (cap.status === 'active') {
++ const audience = m.id === 'network' ? principals.SELF_AUDIENCE : `openvibe.${m.id}`;
++ assert.strictEqual(granted.get(id), audience, `services is granted ${id} for ${audience}`);
++ checked++;
++ }
++}
++for (const [id] of granted) {
++ const cap = contracts.capabilities.get(id);
++ assert.ok(cap && cap.status !== 'retired', `services holds ${id}, which the contracts do not offer`);
++ assert.ok(!revoked.has(id), `${id} is both granted and revoked`);
++}
++assert.ok(checked >= 4, `at least Network, Media, Events and Host are active authorities (${checked})`);
++console.log(`services resource grants: ${checked} active authorities granted, ${granted.size} grants in all`);