openvibers/openvibe.network Commits
Live may run media.analyze on OpenVibe.AI (roadmap WS-O task 5): its ai.run.create and ai.run.read grants for openvibe.ai carry the namespaces live.*, network.site_copy and media.analyze (AI 67fb2a4, local-first VOD and clip analysis). test/principals.test.js pins Live's AI token: those two capabilities and those three namespaces, nothing else.
Changes
@@@ -139,10 +139,10 @@ const DEFAULT_GRANTS = [['codes', 'events.event.publish', 'openvibe.events', []],['ai', 'events.event.publish', 'openvibe.events', []], // ai.run.* (AI server/events.js, 2026-09-24)// Wave 13: Live's AI features run as OpenVibe.AI workflows (AI_SERVICE=remote in live.env).-- // Live runs its own live.* workflows and, as the footer-copy fallback, network.site_copy-- // (OpenVibe.AI fails closed on a token with no ns).-- ['live', 'ai.run.create', 'openvibe.ai', ['live.*', 'network.site_copy']],-- ['live', 'ai.run.read', 'openvibe.ai', ['live.*', 'network.site_copy']],++ // Live runs its own live.* workflows, network.site_copy as the footer-copy fallback, and media.analyze++ // (local-first VOD/clip analysis, roadmap WS-O task 5) (OpenVibe.AI fails closed on a token with no ns).++ ['live', 'ai.run.create', 'openvibe.ai', ['live.*', 'network.site_copy', 'media.analyze']],++ ['live', 'ai.run.read', 'openvibe.ai', ['live.*', 'network.site_copy', 'media.analyze']],// Wave 3: producers publish to OpenVibe.Events (their own source only, enforced by Events)....['live', 'media', 'network', 'community', 'billing', 'chat', 'tools', 'games', 'search', 'sources', 'tips'].map(c => [c, 'events.event.publish', 'openvibe.events', []]),// Wave 14: Search subscribes to <owner>.index_document.* deliveries.@@@ -54,6 +54,11 @@ const server = http.createServer(app);assert.ok(validate('identity.service-token-claims@1', claims.claims).valid);assert.ok(claims.claims.exp - claims.claims.iat <= 300, 'short-lived');const full = t.body.access_token;++ // Live's OpenVibe.AI token: its own workflows, the footer copy and media.analyze (WS-O task 5), nothing else.++ t = await token({ client_id: 'live', client_secret: 'live-secret', audience: 'openvibe.ai' });++ const aiClaims = serviceAuth.verifyServiceToken(t.body.access_token, { publicKey: keys.publicKey, issuer: ISSUER, audience: 'openvibe.ai' });++ assert.ok(aiClaims.ok, aiClaims.reason);++ assert.deepStrictEqual([aiClaims.claims.cap, aiClaims.claims.ns], [['ai.run.create', 'ai.run.read'], ['live.*', 'network.site_copy', 'media.analyze']]);t = await token({ client_id: 'live', client_secret: 'wrong', audience: 'openvibe.network' });assert.strictEqual(t.status, 401); assert.strictEqual(t.body.error, 'invalid_client');
@@ -139,10 +139,10 @@ const DEFAULT_GRANTS = [
@@ -139,10 +139,10 @@ const DEFAULT_GRANTS = [
['codes', 'events.event.publish', 'openvibe.events', []],
['codes', 'events.event.publish', 'openvibe.events', []],
['ai', 'events.event.publish', 'openvibe.events', []], // ai.run.* (AI server/events.js, 2026-09-24)
['ai', 'events.event.publish', 'openvibe.events', []], // ai.run.* (AI server/events.js, 2026-09-24)
// Wave 13: Live's AI features run as OpenVibe.AI workflows (AI_SERVICE=remote in live.env).
// Wave 13: Live's AI features run as OpenVibe.AI workflows (AI_SERVICE=remote in live.env).
- // Live runs its own live.* workflows and, as the footer-copy fallback, network.site_copy
- // (OpenVibe.AI fails closed on a token with no ns).
- ['live', 'ai.run.create', 'openvibe.ai', ['live.*', 'network.site_copy']],
- ['live', 'ai.run.read', 'openvibe.ai', ['live.*', 'network.site_copy']],
+ // Live runs its own live.* workflows, network.site_copy as the footer-copy fallback, and media.analyze
+ // (local-first VOD/clip analysis, roadmap WS-O task 5) (OpenVibe.AI fails closed on a token with no ns).
+ ['live', 'ai.run.create', 'openvibe.ai', ['live.*', 'network.site_copy', 'media.analyze']],
+ ['live', 'ai.run.read', 'openvibe.ai', ['live.*', 'network.site_copy', 'media.analyze']],
// Wave 3: producers publish to OpenVibe.Events (their own source only, enforced by Events).
// Wave 3: producers publish to OpenVibe.Events (their own source only, enforced by Events).
...['live', 'media', 'network', 'community', 'billing', 'chat', 'tools', 'games', 'search', 'sources', 'tips'].map(c => [c, 'events.event.publish', 'openvibe.events', []]),
...['live', 'media', 'network', 'community', 'billing', 'chat', 'tools', 'games', 'search', 'sources', 'tips'].map(c => [c, 'events.event.publish', 'openvibe.events', []]),
// Wave 14: Search subscribes to <owner>.index_document.* deliveries.
// Wave 14: Search subscribes to <owner>.index_document.* deliveries.
@@ -54,6 +54,11 @@ const server = http.createServer(app);
@@ -54,6 +54,11 @@ const server = http.createServer(app);
assert.ok(validate('identity.service-token-claims@1', claims.claims).valid);
assert.ok(validate('identity.service-token-claims@1', claims.claims).valid);
assert.ok(claims.claims.exp - claims.claims.iat <= 300, 'short-lived');
assert.ok(claims.claims.exp - claims.claims.iat <= 300, 'short-lived');
const full = t.body.access_token;
const full = t.body.access_token;
+ // Live's OpenVibe.AI token: its own workflows, the footer copy and media.analyze (WS-O task 5), nothing else.
+ t = await token({ client_id: 'live', client_secret: 'live-secret', audience: 'openvibe.ai' });
+ const aiClaims = serviceAuth.verifyServiceToken(t.body.access_token, { publicKey: keys.publicKey, issuer: ISSUER, audience: 'openvibe.ai' });
+ assert.ok(aiClaims.ok, aiClaims.reason);
+ assert.deepStrictEqual([aiClaims.claims.cap, aiClaims.claims.ns], [['ai.run.create', 'ai.run.read'], ['live.*', 'network.site_copy', 'media.analyze']]);
t = await token({ client_id: 'live', client_secret: 'wrong', audience: 'openvibe.network' });
t = await token({ client_id: 'live', client_secret: 'wrong', audience: 'openvibe.network' });
assert.strictEqual(t.status, 401); assert.strictEqual(t.body.error, 'invalid_client');
assert.strictEqual(t.status, 401); assert.strictEqual(t.body.error, 'invalid_client');