openvibers/openvibe.network Commits
The ADR-033 sweep answers: the grants the new services need (#433)
Changes
@@@ -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 (PostgreSQLand 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 -->@@@ -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",@@@ -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"@@@ -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",@@@ -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).@@@ -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);}nnew file mode 100644@@@ -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');@@@ -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
@@ -50,7 +50,7 @@ Identity and account service for the OpenVibe network. Manages user accounts, OA
@@ -50,7 +50,7 @@ Identity and account service for the OpenVibe network. Manages user accounts, OA
## Depends on
## 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
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`
- OpenVibe.Events (Network's outbox relay; the events it consumes), OpenVibe.AI (the `network.site_copy`
workflow), OpenVibe.Blog (the changelog feed it proxies)
workflow), OpenVibe.Blog (the changelog feed it proxies)
@@ -784,6 +784,6 @@ MIT (`license` in [package.json](package.json)).
@@ -784,6 +784,6 @@ MIT (`license` in [package.json](package.json)).
<!-- versions:start -->
<!-- versions:start -->
- openvibe-contracts: v0.238.0
- openvibe-contracts: v0.238.0
-- openvibe-sdk: v0.67.0
+- openvibe-sdk: v0.74.0
- openvibe-shared: v4.47.2
- openvibe-shared: v4.47.2
<!-- versions:end -->
<!-- versions:end -->
@@ -7,7 +7,7 @@
@@ -7,7 +7,7 @@
"port": 4000,
"port": 4000,
"domain": "openvibe.network (with auth., api., admin., my. and themes.openvibe.network in its manifest)",
"domain": "openvibe.network (with auth., api., admin., my. and themes.openvibe.network in its manifest)",
"contracts": "openvibe-contracts v0.238.0",
"contracts": "openvibe-contracts v0.238.0",
- "sdk": "openvibe-sdk v0.67.0",
+ "sdk": "openvibe-sdk v0.74.0",
"shared": "openvibe-shared v4.47.2",
"shared": "openvibe-shared v4.47.2",
"capabilities": [
"capabilities": [
"identity.subject.resolve",
"identity.subject.resolve",
@@ -21,7 +21,7 @@
@@ -21,7 +21,7 @@
"jsonwebtoken": "^9.0.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
"multer": "^1.4.5-lts.1",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.238.0",
"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",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.47.2",
"pg": "^8.23.0",
"pg": "^8.23.0",
"uuid": "^11.1.0",
"uuid": "^11.1.0",
@@ -1611,9 +1611,9 @@
@@ -1611,9 +1611,9 @@
}
}
},
},
"node_modules/openvibe-sdk": {
"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",
"license": "MIT",
"bin": {
"bin": {
"openvibe-download": "bin/openvibe-download.js"
"openvibe-download": "bin/openvibe-download.js"
@@ -25,7 +25,7 @@
@@ -25,7 +25,7 @@
"jsonwebtoken": "^9.0.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
"multer": "^1.4.5-lts.1",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.238.0",
"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",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.47.2",
"pg": "^8.23.0",
"pg": "^8.23.0",
"uuid": "^11.1.0",
"uuid": "^11.1.0",
@@ -155,7 +155,15 @@ const DEFAULT_GRANTS = [
@@ -155,7 +155,15 @@ const DEFAULT_GRANTS = [
// subscribed first through Network#250). Billing since 2026-10-17 (the money ledger: balances and money records;
// 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
// 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
// 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',
...['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) => [
'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, []],
[svc, 'network.account.export.contribute', SELF_AUDIENCE, []],
@@ -250,6 +258,10 @@ const DEFAULT_GRANTS = [
@@ -250,6 +258,10 @@ const DEFAULT_GRANTS = [
// Coupons subscribes to the sources it watches (OpenVibe.Coupons scripts/subscribe.js), as Deals does.
// Coupons subscribes to the sources it watches (OpenVibe.Coupons scripts/subscribe.js), as Deals does.
['coupons', 'events.subscription.manage', 'openvibe.events', []],
['coupons', 'events.subscription.manage', 'openvibe.events', []],
['coupons', 'sources.item.read', 'openvibe.sources', []],
['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', []],
['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)
// 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).
// their submissions: Host subscribes to the two account events first (its export part and deletion grants follow).
@@ -93,11 +93,11 @@ const server = http.createServer(app);
@@ -93,11 +93,11 @@ const server = http.createServer(app);
// ── Export ──
// ── Export ──
const seeded = await accountData.expectedServices(db, 'network.account.export.contribute');
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
// 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).
// 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']) {
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);
await db.prepare('UPDATE principal_grants SET revoked_at = ov_now() WHERE client_id = ? AND capability = ? AND revoked_at IS NULL').run(svc, cap);
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,27 @@
@@ -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');
@@ -79,10 +79,11 @@ const server = http.createServer(app);
@@ -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']]);
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
// 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' });
t = await token({ client_id: 'search', client_secret: 'search-secret', audience: 'openvibe.network' });
assert.strictEqual(t.status, 200, JSON.stringify(t.body));
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;
const searchToken = t.body.access_token;
// The Search Fabric's embedder (OpenVibe.Search server/fabric/embed.js, contracts 0.234.0) runs AI workflow
// The Search Fabric's embedder (OpenVibe.Search server/fabric/embed.js, contracts 0.234.0) runs AI workflow