openvibers/openvibe.network Commits

Network: AI may use project secrets (network.secret.use) for an app's own provider key (#201)

OP OpenVibers committed 29 days ago · 4f7fba1065 · 2 files, +5 −0

**Network grants `ai` the capability `network.secret.use`, so an app's run on OpenVibe.AI can use its project's own provider key (AI#74).**

**Why:** AI#74 lets a developer app name its project's own OpenAI-format provider on `/v1/chat/completions` (`x_openvibe.provider { base_url, model, secret }`). AI then asks `POST /internal/secrets/use` to reveal that secret to the provider's origin for the one run. Without this grant, AI's token cannot reach the endpoint.

**Change (`server/identity/principals.js`):** a new default grant row `['ai', 'network.secret.use', SELF_AUDIENCE, []]`, beside Actor's and Watch's. The capability only opens the endpoint. Each secret's project grant still decides whether `ai` may use it, for `reveal`, and to which https origins. A project admin sets that in OpenVibe.Services → Secrets.

**Test:** `grants-admin.test.js` checks that exactly Actor, AI and Watch hold `network.secret.use` on Network's own audience. `project-secrets.test.js` and the 32 files that reach `principals.js` pass.

After deploy, AI's next token carries the capability. The default grant is seeded once; `ai` never had it revoked, so no restore is needed.

Changes

ddiff --git a/server/identity/principals.js b/server/identity/principals.js
iindex eb0a0fd7..c29d180f 100644
---- a/server/identity/principals.js
++++ b/server/identity/principals.js
@@@ -446,6 +446,9 @@ const DEFAULT_GRANTS = [
// Watch reveals a source credential to its source's origin, and signs a webhook action, with a project secret its
// watch names (Contracts 0.179.0); each secret's project grant decides.
['watch', 'network.secret.use', SELF_AUDIENCE, []],
++ // AI reveals a project's own provider key to that provider's origin for one run of an app that names it on
++ // /v1/chat/completions (x_openvibe.provider, AI#74); each secret's project grant decides.
++ ['ai', 'network.secret.use', SELF_AUDIENCE, []],
];
// Grants withdrawn by decision; applied at every boot so an old default can't come back.
ddiff --git a/test/grants-admin.test.js b/test/grants-admin.test.js
iindex d7844c5a..cfd757ee 100644
---- a/test/grants-admin.test.js
++++ b/test/grants-admin.test.js
@@@ -36,6 +36,8 @@ for (const [client, cap, aud] of principals.DEFAULT_GRANTS) {
assert.strictEqual(aud, `openvibe.${c.owner}`, `default grant ${client} ${cap} → ${aud}`);
}
// Trade's scripts/subscribe.js (Trade #9) manages its Events subscriptions like the other publishers.
++assert.deepStrictEqual(principals.DEFAULT_GRANTS.filter(([, cap]) => cap === 'network.secret.use').map(([client, , aud]) => [client, aud]).sort(),
++ [['actor', principals.SELF_AUDIENCE], ['ai', principals.SELF_AUDIENCE], ['watch', principals.SELF_AUDIENCE]], 'project secrets: Actor signs, Watch and AI (an app\'s own provider, AI#74) use them');
assert.ok(principals.DEFAULT_GRANTS.some(([client, cap, aud, scopes]) => client === 'trade' && cap === 'events.subscription.manage' && aud === 'openvibe.events' && Array.isArray(scopes) && !scopes.length), 'Trade holds events.subscription.manage on openvibe.events');
assert.strictEqual((await events()).length, 0, 'the seed is not announced');