openvibers/openvibe.network Commits

The ADR-033 sweep answers: the grants the new services need (#433)

OP OpenVibers committed 14 days ago · c20f60ffc1 · 8 files, +54 −14

The services whose account-data module landed in the sweep push their export
part to Network and confirm their deletion, and Network waits for exactly the
services that hold the grant. So the seven new ones get it: AI (its
conversations and runs), Coupons (its claims and coupons), News (its readers'
lists), Run (its jobs and runs), Search (its saved searches and documents),
Sources (its items and feeds) and Events (the bus itself keeps the events a
person caused).

Run, Sources and Events subscribe to the two account topics themselves, through
openvibe-sdk/account-data (startSubscriptions creates the subscription on the
bus), so they also get the scope that creates it on openvibe.events.

Each service subscribed before this grant, as the others did. The expected
answerers are asserted in test/account-data.test.js, and
test/adr033-sweep-grants.test.js holds the list.

Changes

ddiff --git a/README.md b/README.md
iindex e1205042..f15fa606 100644
---- a/README.md
++++ b/README.md
@@@ -50,7 +50,7 @@ Identity and account service for the OpenVibe network. Manages user accounts, OA
## Depends on
--- `openvibe-contracts` v0.222.0 (registry manifests, capability checks), `openvibe-sdk` v0.67.0 (PostgreSQL
++- `openvibe-contracts` v0.222.0 (registry manifests, capability checks), `openvibe-sdk` v0.74.0 (PostgreSQL
and Valkey adapters, events, per-actor limits, universal telemetry), `openvibe-shared` v4.30.0, pinned by release tarball
- OpenVibe.Events (Network's outbox relay; the events it consumes), OpenVibe.AI (the `network.site_copy`
workflow), OpenVibe.Blog (the changelog feed it proxies)
@@@ -784,6 +784,6 @@ MIT (`license` in [package.json](package.json)).
<!-- versions:start -->
- openvibe-contracts: v0.238.0
--- openvibe-sdk: v0.67.0
++- openvibe-sdk: v0.74.0
- openvibe-shared: v4.47.2
<!-- versions:end -->
ddiff --git a/STATUS.json b/STATUS.json
iindex 025b9002..c8870dac 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -7,7 +7,7 @@
"port": 4000,
"domain": "openvibe.network (with auth., api., admin., my. and themes.openvibe.network in its manifest)",
"contracts": "openvibe-contracts v0.238.0",
-- "sdk": "openvibe-sdk v0.67.0",
++ "sdk": "openvibe-sdk v0.74.0",
"shared": "openvibe-shared v4.47.2",
"capabilities": [
"identity.subject.resolve",
ddiff --git a/package-lock.json b/package-lock.json
iindex aba02991..f969b827 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -21,7 +21,7 @@
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.238.0",
-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.67.0",
++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.74.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.47.2",
"pg": "^8.23.0",
"uuid": "^11.1.0",
@@@ -1611,9 +1611,9 @@
}
},
"node_modules/openvibe-sdk": {
-- "version": "0.67.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.67.0",
-- "integrity": "sha512-s7NzjaNcowVcOIiup2z0ze+xsq/qnr++3pHSI8JzirqJPQ5JsBSUF5RkjOHyfk9bRKlmhY0fsNS6x4dsYylyqw==",
++ "version": "0.74.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.74.0",
++ "integrity": "sha512-DO9KVPnUriY/I4lX6ZEJtXaUvRDbJyqnqJxvlpl+zkNGFILpmuBXVJogNOCfK071Qci1gg7C4Iw1Xrseklcajg==",
"license": "MIT",
"bin": {
"openvibe-download": "bin/openvibe-download.js"
ddiff --git a/package.json b/package.json
iindex f28ebccf..488d378b 100644
---- a/package.json
++++ b/package.json
@@@ -25,7 +25,7 @@
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.238.0",
-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.67.0",
++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.74.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.47.2",
"pg": "^8.23.0",
"uuid": "^11.1.0",
ddiff --git a/server/identity/principals.js b/server/identity/principals.js
iindex 236fcec6..ca4389c3 100644
---- a/server/identity/principals.js
++++ b/server/identity/principals.js
@@@ -155,7 +155,15 @@ const DEFAULT_GRANTS = [
// subscribed first through Network#250). Billing since 2026-10-17 (the money ledger: balances and money records;
// Billing#77, subscribed first through Network#263). Reviews since 2026-10-17 (members' reviews, Reviews#76; its
// account-data consumer and subscriptions shipped first in Reviews#77). Host since 2026-10-19 (the console's sealed hosting
-- // tokens and actions, benchmarks' submissions; Host#120, subscribed first through Network#284).
++ // tokens and actions, benchmarks' submissions; Host#120, subscribed first through Network#284). AI since 2026-10-30 (its
++ // conversations and runs; AI#206), Coupons since 2026-10-30 (its claims and coupons; Coupons#148), News since 2026-10-30
++ // (its readers' lists; News#147), Run since 2026-10-30 (its jobs and runs; Run#115), Search since 2026-10-30 (its saved
++ // searches and documents; Search#145), Sources since 2026-10-30 (its items and feeds; Sources#146) and Events since
++ // 2026-10-30 (the bus itself keeps the events a person caused; Events#163). Each subscribed before this grant.
++ ...['ai', 'coupons', 'news', 'run', 'search', 'sources', 'events'].flatMap((svc) => [
++ [svc, 'network.account.export.contribute', SELF_AUDIENCE, []],
++ [svc, 'network.account.deletion.confirm', SELF_AUDIENCE, []],
++ ]),
...['live', 'chat', 'community', 'media', 'games', 'actor', 'food', 'help', 'work', 'quest', 'rent', 'media-hub',
'deals', 'trade', 'tips', 'vip', 'watch', 'services', 'wiki', 'blog', 'bot', 'inventory', 'codes', 'play', 'space', 'billing', 'reviews', 'host', 'domains', 'meme', 'vision', 'exchange', 'fun', 'cam', 'bench'].flatMap((svc) => [
[svc, 'network.account.export.contribute', SELF_AUDIENCE, []],
@@@ -250,6 +258,10 @@ const DEFAULT_GRANTS = [
// Coupons subscribes to the sources it watches (OpenVibe.Coupons scripts/subscribe.js), as Deals does.
['coupons', 'events.subscription.manage', 'openvibe.events', []],
['coupons', 'sources.item.read', 'openvibe.sources', []],
++ // The ADR-033 sweep (2026-10-30): Run, Sources and Events subscribe to the two account topics themselves, through
++ // openvibe-sdk/account-data (startSubscriptions creates the subscription on the bus), so they need the scope that
++ // creates it. Events subscribes to its own bus: the account events it stores are the ones it has to answer for.
++ ...['run', 'sources', 'events'].map((svc) => [svc, 'events.subscription.manage', 'openvibe.events', []]),
['host', 'events.event.publish', 'openvibe.events', []],
// The console (Host#120, 2026-10-19) keeps people's sealed hosting tokens and their actions, and benchmarks (Host#117)
// their submissions: Host subscribes to the two account events first (its export part and deletion grants follow).
ddiff --git a/test/account-data.test.js b/test/account-data.test.js
iindex adad2379..33927275 100644
---- a/test/account-data.test.js
++++ b/test/account-data.test.js
@@@ -93,11 +93,11 @@ const server = http.createServer(app);
// ── Export ──
const seeded = await accountData.expectedServices(db, 'network.account.export.contribute');
-- assert.deepStrictEqual(seeded, ['actor', 'bench', 'billing', 'blog', 'bot', 'cam', 'chat', 'codes', 'community', 'deals', 'domains', 'exchange', 'food', 'fun', 'games', 'help', 'host', 'inventory', 'live', 'media', 'media-hub', 'meme', 'play', 'quest', 'rent', 'reviews', 'services', 'space', 'tips', 'trade', 'vip', 'vision', 'watch', 'wiki', 'work'],
-- 'the holders of the contribute grant: the skeleton services answer too since 2026-10-08 and the older ones since 2026-10-09 (openvibe-sdk/account-data); Codes and Play since 2026-10-14 (run tasks; scores and unlocks); Space again since 2026-10-16 (its spaces); Billing since 2026-10-17 (money records); Reviews since 2026-10-17 (members\' reviews); Host since 2026-10-19 (the console and benchmarks); Meme since 2026-10-28 (votes, captions and reports), Vision and Exchange since 2026-10-28 (answers and checks; listings, installs and offers), Fun since 2026-10-28 (quests, plans, votes, checks, badges, rewards and reports), Cam since 2026-10-28 (cameras, grants, photos and stills), Bench since 2026-10-28 (tasks, rounds, entries, judgements and evals)');
++ assert.deepStrictEqual(seeded, ['actor', 'ai', 'bench', 'billing', 'blog', 'bot', 'cam', 'chat', 'codes', 'community', 'coupons', 'deals', 'domains', 'exchange', 'food', 'fun', 'games', 'help', 'host', 'inventory', 'live', 'media', 'media-hub', 'meme', 'news', 'play', 'quest', 'rent', 'reviews', 'services', 'sources', 'space', 'tips', 'trade', 'vip', 'vision', 'watch', 'wiki', 'work'],
++ 'the holders of the contribute grant: the skeleton services answer too since 2026-10-08 and the older ones since 2026-10-09 (openvibe-sdk/account-data); Codes and Play since 2026-10-14 (run tasks; scores and unlocks); Space again since 2026-10-16 (its spaces); Billing since 2026-10-17 (money records); Reviews since 2026-10-17 (members\' reviews); Host since 2026-10-19 (the console and benchmarks); Meme since 2026-10-28 (votes, captions and reports), Vision and Exchange since 2026-10-28 (answers and checks; listings, installs and offers), Fun since 2026-10-28 (quests, plans, votes, checks, badges, rewards and reports), Cam since 2026-10-28 (cameras, grants, photos and stills), Bench since 2026-10-28 (tasks, rounds, entries, judgements and evals); AI, Coupons, News and Sources since 2026-10-30 (the ADR-033 sweep: their conversations and runs, their claims and coupons, their readers\' lists and their items and feeds)');
// The scenarios below follow five services; the newer ones are set aside here, exactly as an operator would
// revoke a service's grant (the same rows, the same revoked_at).
-- for (const svc of ['actor', 'bench', 'billing', 'blog', 'bot', 'cam', 'codes', 'deals', 'domains', 'exchange', 'food', 'fun', 'help', 'host', 'inventory', 'media-hub', 'meme', 'play', 'quest', 'rent', 'reviews', 'services', 'space', 'tips', 'trade', 'vip', 'vision', 'watch', 'wiki', 'work']) {
++ for (const svc of ['actor', 'ai', 'bench', 'billing', 'blog', 'bot', 'cam', 'codes', 'coupons', 'deals', 'domains', 'exchange', 'food', 'fun', 'help', 'host', 'inventory', 'media-hub', 'meme', 'news', 'play', 'quest', 'rent', 'reviews', 'services', 'sources', 'space', 'tips', 'trade', 'vip', 'vision', 'watch', 'wiki', 'work']) {
for (const cap of ['network.account.export.contribute', 'network.account.deletion.confirm']) {
await db.prepare('UPDATE principal_grants SET revoked_at = ov_now() WHERE client_id = ? AND capability = ? AND revoked_at IS NULL').run(svc, cap);
}
ddiff --git a/test/adr033-sweep-grants.test.js b/test/adr033-sweep-grants.test.js
nnew file mode 100644
iindex 00000000..9b3304d8
---- /dev/null
++++ b/test/adr033-sweep-grants.test.js
@@@ -0,0 +1,27 @@
++/**
++ * The ADR-033 sweep (2026-10-30): the services whose account-data module landed in the sweep answer for what they
++ * keep about a person, so Network waits for them: each holds network.account.export.contribute (it pushes its export
++ * part) and network.account.deletion.confirm (it confirms what it erased) on the network audience.
++ *
++ * The three that subscribe to the two account topics themselves, through openvibe-sdk/account-data (startSubscriptions
++ * creates the subscription on the bus), also hold events.subscription.manage on openvibe.events: Run (its jobs and
++ * runs), Sources (its items and feeds) and Events (the bus itself keeps the events a person caused).
++ *
++ * node test/adr033-sweep-grants.test.js
++ */
++'use strict';
++const assert = require('assert');
++const principals = require('../server/identity/principals');
++
++const holds = (svc, cap, aud) => principals.DEFAULT_GRANTS.some((g) => g[0] === svc && g[1] === cap && g[2] === aud);
++
++for (const svc of ['ai', 'coupons', 'news', 'run', 'search', 'sources', 'events']) {
++ assert.ok(holds(svc, 'network.account.export.contribute', principals.SELF_AUDIENCE), `${svc} answers the account export`);
++ assert.ok(holds(svc, 'network.account.deletion.confirm', principals.SELF_AUDIENCE), `${svc} confirms the account deletion`);
++}
++
++for (const svc of ['run', 'sources', 'events']) {
++ assert.ok(holds(svc, 'events.subscription.manage', 'openvibe.events'), `${svc} subscribes to the two account topics`);
++}
++
++console.log('adr033 sweep grants: ok');
ddiff --git a/test/principals.test.js b/test/principals.test.js
iindex 4e03427c..33a6fbd9 100644
---- a/test/principals.test.js
++++ b/test/principals.test.js
@@@ -79,10 +79,11 @@ const server = http.createServer(app);
assert.deepStrictEqual([aiClaims.claims.cap, aiClaims.claims.ns], [['ai.credential.manage', 'ai.quota.attribution.manage', 'ai.run.create', 'ai.run.read'], ['live.*', 'network.site_copy', 'media.analyze']]);
// Plan T9: Search's saved-search notifier resolves the saved search's owner and pushes one notification
-- // per new match through Network, and holds nothing else there.
++ // per new match through Network. Since 2026-10-30 (the ADR-033 sweep, Search#145) Search also answers the
++ // account export and confirms the deletion.
t = await token({ client_id: 'search', client_secret: 'search-secret', audience: 'openvibe.network' });
assert.strictEqual(t.status, 200, JSON.stringify(t.body));
-- assert.deepStrictEqual(t.body.scope.split(' '), ['identity.subject.resolve', 'network.notifications.push']);
++ assert.deepStrictEqual(t.body.scope.split(' '), ['identity.subject.resolve', 'network.account.deletion.confirm', 'network.account.export.contribute', 'network.notifications.push']);
const searchToken = t.body.access_token;
// The Search Fabric's embedder (OpenVibe.Search server/fabric/embed.js, contracts 0.234.0) runs AI workflow