openvibers/openvibe.network Commits
Allow Mail to run private Inbox AI for mailbox owners (#675)
Changes
@@@ -712,6 +712,9 @@ const DEFAULT_GRANTS = [// OpenVibe.Vision (contracts 0.235.0): model calls through AI's OpenAI-compatible endpoint (workflow ai.complete),// its account-event subscriptions, and the export/deletion pair above. It has no public records, so no Search grant.['vision', 'ai.run.create', 'openvibe.ai', ['ai.complete']],++ // Mail's opt-in Inbox AI runs only ai.complete through OpenVibe.AI, attributed to the mailbox owner.++ ['mail', 'ai.run.create', 'openvibe.ai', ['ai.complete']],++ ['mail', 'ai.run.read', 'openvibe.ai', ['ai.complete']],['vision', 'events.subscription.manage', 'openvibe.events', []],// VIS-6: the operator review, its evidence view and the decision that releases Media's evidence hold.// The media namespaces are the first-party tenants whose objects can be reviewed.@@@ -126,6 +126,10 @@ const server = http.createServer(app);// Vision also publishes its own vision.result.ready and vision.alert (VIS-5; granted 11-02).assert.strictEqual(t.body.scope, client === 'vision' ? 'events.event.publish events.subscription.manage' : 'events.subscription.manage', `${client} subscribes to the account events at boot`);}++++ await db.prepare('UPDATE oauth_clients SET client_secret = ? WHERE client_id = ?').run('mail-secret', 'mail');++ t = await token({ client_id: 'mail', client_secret: 'mail-secret', audience: 'openvibe.ai' });++ assert.deepStrictEqual(t.body.scope.split(' ').sort(), ['ai.run.create', 'ai.run.read']);t = await token({ client_id: 'vision', client_secret: 'vision-secret', audience: 'openvibe.ai' });assert.strictEqual(t.status, 200, JSON.stringify(t.body));const visionAiClaims = serviceAuth.verifyServiceToken(t.body.access_token, { publicKey: keys.publicKey, issuer: ISSUER, audience: 'openvibe.ai' });
@@ -712,6 +712,9 @@ const DEFAULT_GRANTS = [
@@ -712,6 +712,9 @@ const DEFAULT_GRANTS = [
// OpenVibe.Vision (contracts 0.235.0): model calls through AI's OpenAI-compatible endpoint (workflow ai.complete),
// OpenVibe.Vision (contracts 0.235.0): model calls through AI's OpenAI-compatible endpoint (workflow ai.complete),
// its account-event subscriptions, and the export/deletion pair above. It has no public records, so no Search grant.
// its account-event subscriptions, and the export/deletion pair above. It has no public records, so no Search grant.
['vision', 'ai.run.create', 'openvibe.ai', ['ai.complete']],
['vision', 'ai.run.create', 'openvibe.ai', ['ai.complete']],
+ // Mail's opt-in Inbox AI runs only ai.complete through OpenVibe.AI, attributed to the mailbox owner.
+ ['mail', 'ai.run.create', 'openvibe.ai', ['ai.complete']],
+ ['mail', 'ai.run.read', 'openvibe.ai', ['ai.complete']],
['vision', 'events.subscription.manage', 'openvibe.events', []],
['vision', 'events.subscription.manage', 'openvibe.events', []],
// VIS-6: the operator review, its evidence view and the decision that releases Media's evidence hold.
// VIS-6: the operator review, its evidence view and the decision that releases Media's evidence hold.
// The media namespaces are the first-party tenants whose objects can be reviewed.
// The media namespaces are the first-party tenants whose objects can be reviewed.
@@ -126,6 +126,10 @@ const server = http.createServer(app);
@@ -126,6 +126,10 @@ const server = http.createServer(app);
// Vision also publishes its own vision.result.ready and vision.alert (VIS-5; granted 11-02).
// Vision also publishes its own vision.result.ready and vision.alert (VIS-5; granted 11-02).
assert.strictEqual(t.body.scope, client === 'vision' ? 'events.event.publish events.subscription.manage' : 'events.subscription.manage', `${client} subscribes to the account events at boot`);
assert.strictEqual(t.body.scope, client === 'vision' ? 'events.event.publish events.subscription.manage' : 'events.subscription.manage', `${client} subscribes to the account events at boot`);
}
}
+
+ await db.prepare('UPDATE oauth_clients SET client_secret = ? WHERE client_id = ?').run('mail-secret', 'mail');
+ t = await token({ client_id: 'mail', client_secret: 'mail-secret', audience: 'openvibe.ai' });
+ assert.deepStrictEqual(t.body.scope.split(' ').sort(), ['ai.run.create', 'ai.run.read']);
t = await token({ client_id: 'vision', client_secret: 'vision-secret', audience: 'openvibe.ai' });
t = await token({ client_id: 'vision', client_secret: 'vision-secret', audience: 'openvibe.ai' });
assert.strictEqual(t.status, 200, JSON.stringify(t.body));
assert.strictEqual(t.status, 200, JSON.stringify(t.body));
const visionAiClaims = serviceAuth.verifyServiceToken(t.body.access_token, { publicKey: keys.publicKey, issuer: ISSUER, audience: 'openvibe.ai' });
const visionAiClaims = serviceAuth.verifyServiceToken(t.body.access_token, { publicKey: keys.publicKey, issuer: ISSUER, audience: 'openvibe.ai' });