openvibers/openvibe.network Commits
OpenVibe.Surf live (client surf, openvibe.surf unparked); Codes publishes codes.task.* again; contracts v0.232.0 (#397)
Changes
@@@ -151,28 +151,6 @@ server {return 302 https://openvibe.games/;}--# openvibe.surf → Games--server {-- listen 80;-- listen [::]:80;-- server_name openvibe.surf www.openvibe.surf;-- return 302 https://openvibe.games/;--}----server {-- listen 443 ssl;-- listen [::]:443 ssl;-- http2 on;-- server_name openvibe.surf www.openvibe.surf;-- ssl_certificate /etc/letsencrypt/live/openvibe.surf/fullchain.pem;-- ssl_certificate_key /etc/letsencrypt/live/openvibe.surf/privkey.pem;-- add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;-- add_header X-Content-Type-Options "nosniff" always;-- add_header Referrer-Policy "strict-origin-when-cross-origin" always;-- access_log off;-- return 302 https://openvibe.games/;--}--# openvibe.fun → Gamesserver {listen 80;@@@ -20,7 +20,7 @@"iovalkey": "^0.4.0","jsonwebtoken": "^9.0.0","multer": "^1.4.5-lts.1",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.230.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.232.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.67.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.31.0","pg": "^8.23.0",@@@ -1320,9 +1320,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.230.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.230.0",-- "integrity": "sha512-wvzSYV0GKfHV6UnEKtqDAXkrl3yimdWe/KHM0jeU0JWkrRo93zGNAZ2fRPwATBeihITVE0b973dM899U/YOzgw==",++ "version": "0.232.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.232.0",++ "integrity": "sha512-6HI5nn//nAUEDdG4xRTAlcb40oc/6yO1nx7R5exGZ6Bo3mL5fJ5szw9IONddoLN6qJsO1CXzB5TzTqkBAh3OzQ==","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -24,7 +24,7 @@"iovalkey": "^0.4.0","jsonwebtoken": "^9.0.0","multer": "^1.4.5-lts.1",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.230.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.232.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.67.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.31.0","pg": "^8.23.0",@@@ -124,6 +124,8 @@ async function seedDb(db, { log = console } = {}) {{ client_id: 'design', name: 'OpenVibe.Design', redirect_uris: ['https://openvibe.design/auth/callback'] },// Schedules, reminders, calendar feeds and the brief (plan 2026-10-28 §5.3); created first as Today's principal.{ client_id: 'today', name: 'OpenVibe.Today', redirect_uris: ['https://openvibe.today/auth/callback'] },++ // Ride maps, surf the web, surf what's on (plan 2026-10-28 §5.6); a stateless front that signs people in for Web.++ { client_id: 'surf', name: 'OpenVibe.Surf', redirect_uris: ['https://openvibe.surf/auth/callback'] },{ client_id: 'work', name: 'OpenVibe.Work', redirect_uris: ['https://openvibe.work/auth/callback'] },// The shared quest log, listings people post, and Watch's public site (plan T18 step 8, T19).{ client_id: 'quest', name: 'OpenVibe.Quest', redirect_uris: ['https://openvibe.quest/auth/callback'] },@@@ -389,6 +389,8 @@ const DEFAULT_GRANTS = [['bot', 'network.resource.report', SELF_AUDIENCE, []],['run', 'network.registry.read', SELF_AUDIENCE, []],...['run.job.submit', 'run.job.read', 'run.job.stream', 'run.job.cancel'].map((c) => ['codes', c, 'openvibe.run', []]),++ // Plan 2026-10-28 §6: Codes announces its coding tasks' state changes (codes.task.*, contracts 0.232.0).++ ['codes', 'events.event.publish', 'openvibe.events', []],// Plan T16/T17 (contracts 0.162.0): a person's OpenVibe.Actor agent routes coding work to OpenVibe.Codes, which runs// it on that person's own machine (X-OV-Subject); Codes shows Actor only the tasks Actor started for that person....['codes.task.start', 'codes.task.read', 'codes.task.cancel'].map((c) => ['actor', c, 'openvibe.codes', []]),@@@ -563,10 +565,9 @@ const REVOKED_GRANTS = [['live', 'network.coins.transfer', SELF_AUDIENCE],// The Live read mirror is retired (2026-10-05): Chat #25 removed the sender, Live the receiver.['chat', 'live.chat_mirror.write', 'openvibe.live'],-- // The developer portal moved from Codes to Services (contracts 0.113.0): Codes owns no resources and publishes no-- // event any more; Services publishes the services.* events and reads its own resources in process.++ // The developer portal moved from Codes to Services (contracts 0.113.0): Codes owns no resources; Services++ // publishes the services.* events and reads its own resources in process. (Codes publishes again since 0.232.0.)['services', 'codes.resource.read', 'openvibe.codes'],-- ['codes', 'events.event.publish', 'openvibe.events'],// The forum returned to Community (owner decision 2026-10-08, contracts 0.118.0): Space keeps only its sign-in// client. Its forum grants go (the forum's account data is Community's). Its account and subscription grants came// back with spaces (RESTORED_GRANTS, 2026-10-16).@@@ -598,6 +599,8 @@ const RESTORED_GRANTS = [// ...and answers account export and deletion for them again (Space#59, subscriptions verified 2026-10-16 20:20 UTC).['space', 'network.account.export.contribute', SELF_AUDIENCE],['space', 'network.account.deletion.confirm', SELF_AUDIENCE],++ // Codes publishes codes.task.* (contracts 0.232.0); revoked when the developer portal left it (0.113.0).++ ['codes', 'events.event.publish', 'openvibe.events'],];const CHANGED_DEFAULT_NAMESPACES = [@@@ -14,7 +14,7 @@ assert.equal(siteForHost('yt.openvibe.tools').id, 'tools');assert.equal(siteForHost('evil.example'), null);assert.strictEqual(svc.ranking().at, null, 'cold start: no use counted yet (the registry\'s featured list says so)');let p = svc.payloadFor('openvibe.media');--assert.deepEqual(p.nav.filter(n => !n.of).map(n => n.id), ['live', 'tools', 'space', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'news', 'reviews', 'tips', 'vip', 'trade', 'host', 'deals', 'coupons', 'stream', 'media-hub', 'bot', 'codes', 'actor', 'food', 'help', 'work', 'quest', 'rent', 'watch', 'inventory', 'events', 'ai', 'search', 'run', 'play', 'zone', 'domains', 'report', 'design', 'today'], 'cold start follows the base order');++assert.deepEqual(p.nav.filter(n => !n.of).map(n => n.id), ['live', 'tools', 'space', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'news', 'reviews', 'tips', 'vip', 'trade', 'host', 'deals', 'coupons', 'stream', 'media-hub', 'bot', 'codes', 'actor', 'food', 'help', 'work', 'quest', 'rent', 'watch', 'inventory', 'events', 'ai', 'search', 'run', 'play', 'zone', 'domains', 'report', 'design', 'today', 'surf'], 'cold start follows the base order');// The launcher: every open site in one of the groups, and the products other services serve on their own domains// right after their site (Pics and Download after Video, Homes after Rent).assert.deepStrictEqual(p.groups.map(g => g.name), ['Streams & places', 'Games & laughs', 'News & answers', 'Make & build', 'Shop & swap', 'People & you', 'For developers'], 'the six neighbourhoods (plan 2026-10-28 §3.1), the building blocks last');@@@ -26,9 +26,9 @@ assert.ok(at('pics') === at('media-hub') + 1 && at('homes') === at('rent') + 1,assert.deepStrictEqual([p.nav[at('media-hub')].icon, p.nav[at('media-hub')].group, p.nav[at('homes')].group, p.nav[at('tools')].group], ['video', 'streams', 'shop', 'make'], 'Video draws the video icon (there is no media-hub icon)');assert.deepStrictEqual(['actor', 'bot', 'run', 'zone'].map((id) => p.nav[at(id)].group), ['make', 'make', 'build', 'build'], 'agents and robots for people, compute and storage for developers');// Games together, items with the account, Alerts with the building blocks, files beside Video (each when its site is open).--[['games', 'games'], ['quest', 'games'], ['inventory', 'people'], ['watch', 'build'], ['download', 'streams'], ['search', 'answers'], ['chat', 'people'], ['domains', 'make'], ['report', 'answers'], ['design', 'make'], ['today', 'answers']]++[['games', 'games'], ['quest', 'games'], ['inventory', 'people'], ['watch', 'build'], ['download', 'streams'], ['search', 'answers'], ['chat', 'people'], ['domains', 'make'], ['report', 'answers'], ['design', 'make'], ['today', 'answers'], ['surf', 'games']].filter(([id]) => at(id) >= 0).forEach(([id, group]) => assert.strictEqual(p.nav[at(id)].group, group, `${id} is in ${group}`));--assert.strictEqual(p.nav.length, 44); // Domains since contracts 0.226.0, Report since 0.227.0, Design since 0.228.0, Today since 0.230.0++assert.strictEqual(p.nav.length, 45); // Domains since contracts 0.226.0, Report since 0.227.0, Design since 0.228.0, Today since 0.230.0, Surf since 0.231.0assert.deepStrictEqual([p.nav[at('stream')].name, p.nav[at('pics')].name], ['Restream', 'Pics'], 'OpenRestream is "Restream" among the menu\'s short names (owner, 2026-10-28)');assert.strictEqual(p.nav.find(n => n.id === 'space').url, 'https://openvibe.space/', 'Space keeps its own home (the forum returned to Community, 0.118.0)');assert.strictEqual(p.nav.find(n => n.id === 'community').url, 'https://openvibe.community/', 'Community: the forum, pastes and Pulse');@@@ -17,7 +17,7 @@ const { SITES } = require('../server/frame/sites');const PINNED = {network: 'live', live: 'live', tools: 'live', media: 'live', games: 'live', community: 'live', events: 'live',billing: 'live', codes: 'live', blog: 'live', wiki: 'live', sites: 'retired', // Sites retired 2026-10-14 (contracts 0.181.0)-- news: 'live', reviews: 'live', deals: 'live', coupons: 'live', trade: 'live', host: 'live', domains: 'live', report: 'live', design: 'live', today: 'live', // News, Reviews, Coupons, Trade live since contracts 0.125.0; Domains since 0.226.0, Report 0.227.0, Design 0.228.0++ news: 'live', reviews: 'live', deals: 'live', coupons: 'live', trade: 'live', host: 'live', domains: 'live', report: 'live', design: 'live', today: 'live', surf: 'live', // News, Reviews, Coupons, Trade live since contracts 0.125.0; Domains since 0.226.0, Report 0.227.0, Design 0.228.0tips: 'live', vip: 'live', openre: 'live', search: 'live', sources: 'internal', chat: 'live', 'media-hub': 'live', space: 'live', // a home and redirects since the forum returned to Community (contracts 0.118.0)// tips, vip: their own sites at openvibe.tips and openvibe.vip since contracts 0.200.0 (2026-10-17)ai: 'live', // the public home and developer-app API at ai.openvibe.services since 2026-10-07 (contracts 0.110.0)@@@ -121,7 +121,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch// v0.95.0 Services; Space and AI went live in v0.110.0; Food, Help and Work in v0.116.0; Quest, Rent and Watch in v0.117.0;// Inventory in v0.122.0; none is retired// News, Reviews, Coupons, Trade and OpenRestream in v0.125.0; Run in v0.141.0-- assert.deepStrictEqual(st.exposure_summary, { live: 42, internal: 1, library: 4, repository: 1, placeholder: 0, retired: 1 }); // Sites retired (contracts 0.181.0); Tips and VIP live (0.200.0); Design live (0.228.0)++ assert.deepStrictEqual(st.exposure_summary, { live: 43, internal: 1, library: 4, repository: 1, placeholder: 0, retired: 1 }); // Sites retired (contracts 0.181.0); Tips and VIP live (0.200.0); Design live (0.228.0)// The page: no row for a service whose domain serves a placeholder shows a bare "Up".const html = await fetch(base + '/status').then(x => x.text());@@@ -137,7 +137,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch// 3. The Frame: the nav lists only sites whose domain serves the service; the rest are soon with a reason.const open = SITES.filter(s => s.status === 'open').map(s => s.id);-- assert.deepStrictEqual(open, ['live', 'tools', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'news', 'reviews', 'tips', 'vip', 'trade', 'host', 'deals', 'coupons', 'stream', 'space', 'media-hub', 'bot', 'codes', 'actor', 'food', 'help', 'work', 'quest', 'rent', 'watch', 'inventory', 'events', 'ai', 'search', 'run', 'play', 'zone', 'domains', 'report', 'design', 'today']); // domains: open since 0.226.0, report since 0.227.0, design since 0.228.0 (the 2026-10-28 rooms); tips, vip: open since 0.200.0; zone: a site since 0.173.0; play: a site since 0.172.0; run: a site since 0.141.0; events, ai and search: sites since 0.135.0; news, reviews, trade, coupons and stream (OpenRestream): open since 0.125.0; // by site position; bot: a site since openvibe-contracts 0.106.0; host: live since 0.107.0; space: live since 0.110.0 (the forum, 2026-10-07)++ assert.deepStrictEqual(open, ['live', 'tools', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'news', 'reviews', 'tips', 'vip', 'trade', 'host', 'deals', 'coupons', 'stream', 'space', 'media-hub', 'bot', 'codes', 'actor', 'food', 'help', 'work', 'quest', 'rent', 'watch', 'inventory', 'events', 'ai', 'search', 'run', 'play', 'zone', 'domains', 'report', 'design', 'today', 'surf']); // domains: open since 0.226.0, report since 0.227.0, design since 0.228.0 (the 2026-10-28 rooms); tips, vip: open since 0.200.0; zone: a site since 0.173.0; play: a site since 0.172.0; run: a site since 0.141.0; events, ai and search: sites since 0.135.0; news, reviews, trade, coupons and stream (OpenRestream): open since 0.125.0; // by site position; bot: a site since openvibe-contracts 0.106.0; host: live since 0.107.0; space: live since 0.110.0 (the forum, 2026-10-07)for (const s of SITES) {assert.ok(s.service && PINNED[s.service], `${s.id} names its service`);assert.strictEqual(s.status === 'open', PINNED[s.service] === 'live', `${s.id} nav status follows exposure`);
@@ -151,28 +151,6 @@ server {
@@ -151,28 +151,6 @@ server {
return 302 https://openvibe.games/;
return 302 https://openvibe.games/;
}
}
-# openvibe.surf → Games
-server {
- listen 80;
- listen [::]:80;
- server_name openvibe.surf www.openvibe.surf;
- return 302 https://openvibe.games/;
-}
-
-server {
- listen 443 ssl;
- listen [::]:443 ssl;
- http2 on;
- server_name openvibe.surf www.openvibe.surf;
- ssl_certificate /etc/letsencrypt/live/openvibe.surf/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/openvibe.surf/privkey.pem;
- add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
- add_header X-Content-Type-Options "nosniff" always;
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
- access_log off;
- return 302 https://openvibe.games/;
-}
-
# openvibe.fun → Games
# openvibe.fun → Games
server {
server {
listen 80;
listen 80;
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
"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.230.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.232.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.67.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.31.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.31.0",
"pg": "^8.23.0",
"pg": "^8.23.0",
@@ -1320,9 +1320,9 @@
@@ -1320,9 +1320,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
- "version": "0.230.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.230.0",
- "integrity": "sha512-wvzSYV0GKfHV6UnEKtqDAXkrl3yimdWe/KHM0jeU0JWkrRo93zGNAZ2fRPwATBeihITVE0b973dM899U/YOzgw==",
+ "version": "0.232.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.232.0",
+ "integrity": "sha512-6HI5nn//nAUEDdG4xRTAlcb40oc/6yO1nx7R5exGZ6Bo3mL5fJ5szw9IONddoLN6qJsO1CXzB5TzTqkBAh3OzQ==",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -24,7 +24,7 @@
@@ -24,7 +24,7 @@
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
"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.230.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.232.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.67.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.31.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.31.0",
"pg": "^8.23.0",
"pg": "^8.23.0",
@@ -124,6 +124,8 @@ async function seedDb(db, { log = console } = {}) {
@@ -124,6 +124,8 @@ async function seedDb(db, { log = console } = {}) {
{ client_id: 'design', name: 'OpenVibe.Design', redirect_uris: ['https://openvibe.design/auth/callback'] },
{ client_id: 'design', name: 'OpenVibe.Design', redirect_uris: ['https://openvibe.design/auth/callback'] },
// Schedules, reminders, calendar feeds and the brief (plan 2026-10-28 §5.3); created first as Today's principal.
// Schedules, reminders, calendar feeds and the brief (plan 2026-10-28 §5.3); created first as Today's principal.
{ client_id: 'today', name: 'OpenVibe.Today', redirect_uris: ['https://openvibe.today/auth/callback'] },
{ client_id: 'today', name: 'OpenVibe.Today', redirect_uris: ['https://openvibe.today/auth/callback'] },
+ // Ride maps, surf the web, surf what's on (plan 2026-10-28 §5.6); a stateless front that signs people in for Web.
+ { client_id: 'surf', name: 'OpenVibe.Surf', redirect_uris: ['https://openvibe.surf/auth/callback'] },
{ client_id: 'work', name: 'OpenVibe.Work', redirect_uris: ['https://openvibe.work/auth/callback'] },
{ client_id: 'work', name: 'OpenVibe.Work', redirect_uris: ['https://openvibe.work/auth/callback'] },
// The shared quest log, listings people post, and Watch's public site (plan T18 step 8, T19).
// The shared quest log, listings people post, and Watch's public site (plan T18 step 8, T19).
{ client_id: 'quest', name: 'OpenVibe.Quest', redirect_uris: ['https://openvibe.quest/auth/callback'] },
{ client_id: 'quest', name: 'OpenVibe.Quest', redirect_uris: ['https://openvibe.quest/auth/callback'] },
@@ -389,6 +389,8 @@ const DEFAULT_GRANTS = [
@@ -389,6 +389,8 @@ const DEFAULT_GRANTS = [
['bot', 'network.resource.report', SELF_AUDIENCE, []],
['bot', 'network.resource.report', SELF_AUDIENCE, []],
['run', 'network.registry.read', SELF_AUDIENCE, []],
['run', 'network.registry.read', SELF_AUDIENCE, []],
...['run.job.submit', 'run.job.read', 'run.job.stream', 'run.job.cancel'].map((c) => ['codes', c, 'openvibe.run', []]),
...['run.job.submit', 'run.job.read', 'run.job.stream', 'run.job.cancel'].map((c) => ['codes', c, 'openvibe.run', []]),
+ // Plan 2026-10-28 §6: Codes announces its coding tasks' state changes (codes.task.*, contracts 0.232.0).
+ ['codes', 'events.event.publish', 'openvibe.events', []],
// Plan T16/T17 (contracts 0.162.0): a person's OpenVibe.Actor agent routes coding work to OpenVibe.Codes, which runs
// Plan T16/T17 (contracts 0.162.0): a person's OpenVibe.Actor agent routes coding work to OpenVibe.Codes, which runs
// it on that person's own machine (X-OV-Subject); Codes shows Actor only the tasks Actor started for that person.
// it on that person's own machine (X-OV-Subject); Codes shows Actor only the tasks Actor started for that person.
...['codes.task.start', 'codes.task.read', 'codes.task.cancel'].map((c) => ['actor', c, 'openvibe.codes', []]),
...['codes.task.start', 'codes.task.read', 'codes.task.cancel'].map((c) => ['actor', c, 'openvibe.codes', []]),
@@ -563,10 +565,9 @@ const REVOKED_GRANTS = [
@@ -563,10 +565,9 @@ const REVOKED_GRANTS = [
['live', 'network.coins.transfer', SELF_AUDIENCE],
['live', 'network.coins.transfer', SELF_AUDIENCE],
// The Live read mirror is retired (2026-10-05): Chat #25 removed the sender, Live the receiver.
// The Live read mirror is retired (2026-10-05): Chat #25 removed the sender, Live the receiver.
['chat', 'live.chat_mirror.write', 'openvibe.live'],
['chat', 'live.chat_mirror.write', 'openvibe.live'],
- // The developer portal moved from Codes to Services (contracts 0.113.0): Codes owns no resources and publishes no
- // event any more; Services publishes the services.* events and reads its own resources in process.
+ // The developer portal moved from Codes to Services (contracts 0.113.0): Codes owns no resources; Services
+ // publishes the services.* events and reads its own resources in process. (Codes publishes again since 0.232.0.)
['services', 'codes.resource.read', 'openvibe.codes'],
['services', 'codes.resource.read', 'openvibe.codes'],
- ['codes', 'events.event.publish', 'openvibe.events'],
// The forum returned to Community (owner decision 2026-10-08, contracts 0.118.0): Space keeps only its sign-in
// The forum returned to Community (owner decision 2026-10-08, contracts 0.118.0): Space keeps only its sign-in
// client. Its forum grants go (the forum's account data is Community's). Its account and subscription grants came
// client. Its forum grants go (the forum's account data is Community's). Its account and subscription grants came
// back with spaces (RESTORED_GRANTS, 2026-10-16).
// back with spaces (RESTORED_GRANTS, 2026-10-16).
@@ -598,6 +599,8 @@ const RESTORED_GRANTS = [
@@ -598,6 +599,8 @@ const RESTORED_GRANTS = [
// ...and answers account export and deletion for them again (Space#59, subscriptions verified 2026-10-16 20:20 UTC).
// ...and answers account export and deletion for them again (Space#59, subscriptions verified 2026-10-16 20:20 UTC).
['space', 'network.account.export.contribute', SELF_AUDIENCE],
['space', 'network.account.export.contribute', SELF_AUDIENCE],
['space', 'network.account.deletion.confirm', SELF_AUDIENCE],
['space', 'network.account.deletion.confirm', SELF_AUDIENCE],
+ // Codes publishes codes.task.* (contracts 0.232.0); revoked when the developer portal left it (0.113.0).
+ ['codes', 'events.event.publish', 'openvibe.events'],
];
];
const CHANGED_DEFAULT_NAMESPACES = [
const CHANGED_DEFAULT_NAMESPACES = [
@@ -14,7 +14,7 @@ assert.equal(siteForHost('yt.openvibe.tools').id, 'tools');
@@ -14,7 +14,7 @@ assert.equal(siteForHost('yt.openvibe.tools').id, 'tools');
assert.equal(siteForHost('evil.example'), null);
assert.equal(siteForHost('evil.example'), null);
assert.strictEqual(svc.ranking().at, null, 'cold start: no use counted yet (the registry\'s featured list says so)');
assert.strictEqual(svc.ranking().at, null, 'cold start: no use counted yet (the registry\'s featured list says so)');
let p = svc.payloadFor('openvibe.media');
let p = svc.payloadFor('openvibe.media');
-assert.deepEqual(p.nav.filter(n => !n.of).map(n => n.id), ['live', 'tools', 'space', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'news', 'reviews', 'tips', 'vip', 'trade', 'host', 'deals', 'coupons', 'stream', 'media-hub', 'bot', 'codes', 'actor', 'food', 'help', 'work', 'quest', 'rent', 'watch', 'inventory', 'events', 'ai', 'search', 'run', 'play', 'zone', 'domains', 'report', 'design', 'today'], 'cold start follows the base order');
+assert.deepEqual(p.nav.filter(n => !n.of).map(n => n.id), ['live', 'tools', 'space', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'news', 'reviews', 'tips', 'vip', 'trade', 'host', 'deals', 'coupons', 'stream', 'media-hub', 'bot', 'codes', 'actor', 'food', 'help', 'work', 'quest', 'rent', 'watch', 'inventory', 'events', 'ai', 'search', 'run', 'play', 'zone', 'domains', 'report', 'design', 'today', 'surf'], 'cold start follows the base order');
// The launcher: every open site in one of the groups, and the products other services serve on their own domains
// The launcher: every open site in one of the groups, and the products other services serve on their own domains
// right after their site (Pics and Download after Video, Homes after Rent).
// right after their site (Pics and Download after Video, Homes after Rent).
assert.deepStrictEqual(p.groups.map(g => g.name), ['Streams & places', 'Games & laughs', 'News & answers', 'Make & build', 'Shop & swap', 'People & you', 'For developers'], 'the six neighbourhoods (plan 2026-10-28 §3.1), the building blocks last');
assert.deepStrictEqual(p.groups.map(g => g.name), ['Streams & places', 'Games & laughs', 'News & answers', 'Make & build', 'Shop & swap', 'People & you', 'For developers'], 'the six neighbourhoods (plan 2026-10-28 §3.1), the building blocks last');
@@ -26,9 +26,9 @@ assert.ok(at('pics') === at('media-hub') + 1 && at('homes') === at('rent') + 1,
@@ -26,9 +26,9 @@ assert.ok(at('pics') === at('media-hub') + 1 && at('homes') === at('rent') + 1,
assert.deepStrictEqual([p.nav[at('media-hub')].icon, p.nav[at('media-hub')].group, p.nav[at('homes')].group, p.nav[at('tools')].group], ['video', 'streams', 'shop', 'make'], 'Video draws the video icon (there is no media-hub icon)');
assert.deepStrictEqual([p.nav[at('media-hub')].icon, p.nav[at('media-hub')].group, p.nav[at('homes')].group, p.nav[at('tools')].group], ['video', 'streams', 'shop', 'make'], 'Video draws the video icon (there is no media-hub icon)');
assert.deepStrictEqual(['actor', 'bot', 'run', 'zone'].map((id) => p.nav[at(id)].group), ['make', 'make', 'build', 'build'], 'agents and robots for people, compute and storage for developers');
assert.deepStrictEqual(['actor', 'bot', 'run', 'zone'].map((id) => p.nav[at(id)].group), ['make', 'make', 'build', 'build'], 'agents and robots for people, compute and storage for developers');
// Games together, items with the account, Alerts with the building blocks, files beside Video (each when its site is open).
// Games together, items with the account, Alerts with the building blocks, files beside Video (each when its site is open).
-[['games', 'games'], ['quest', 'games'], ['inventory', 'people'], ['watch', 'build'], ['download', 'streams'], ['search', 'answers'], ['chat', 'people'], ['domains', 'make'], ['report', 'answers'], ['design', 'make'], ['today', 'answers']]
+[['games', 'games'], ['quest', 'games'], ['inventory', 'people'], ['watch', 'build'], ['download', 'streams'], ['search', 'answers'], ['chat', 'people'], ['domains', 'make'], ['report', 'answers'], ['design', 'make'], ['today', 'answers'], ['surf', 'games']]
.filter(([id]) => at(id) >= 0).forEach(([id, group]) => assert.strictEqual(p.nav[at(id)].group, group, `${id} is in ${group}`));
.filter(([id]) => at(id) >= 0).forEach(([id, group]) => assert.strictEqual(p.nav[at(id)].group, group, `${id} is in ${group}`));
-assert.strictEqual(p.nav.length, 44); // Domains since contracts 0.226.0, Report since 0.227.0, Design since 0.228.0, Today since 0.230.0
+assert.strictEqual(p.nav.length, 45); // Domains since contracts 0.226.0, Report since 0.227.0, Design since 0.228.0, Today since 0.230.0, Surf since 0.231.0
assert.deepStrictEqual([p.nav[at('stream')].name, p.nav[at('pics')].name], ['Restream', 'Pics'], 'OpenRestream is "Restream" among the menu\'s short names (owner, 2026-10-28)');
assert.deepStrictEqual([p.nav[at('stream')].name, p.nav[at('pics')].name], ['Restream', 'Pics'], 'OpenRestream is "Restream" among the menu\'s short names (owner, 2026-10-28)');
assert.strictEqual(p.nav.find(n => n.id === 'space').url, 'https://openvibe.space/', 'Space keeps its own home (the forum returned to Community, 0.118.0)');
assert.strictEqual(p.nav.find(n => n.id === 'space').url, 'https://openvibe.space/', 'Space keeps its own home (the forum returned to Community, 0.118.0)');
assert.strictEqual(p.nav.find(n => n.id === 'community').url, 'https://openvibe.community/', 'Community: the forum, pastes and Pulse');
assert.strictEqual(p.nav.find(n => n.id === 'community').url, 'https://openvibe.community/', 'Community: the forum, pastes and Pulse');
@@ -17,7 +17,7 @@ const { SITES } = require('../server/frame/sites');
@@ -17,7 +17,7 @@ const { SITES } = require('../server/frame/sites');
const PINNED = {
const PINNED = {
network: 'live', live: 'live', tools: 'live', media: 'live', games: 'live', community: 'live', events: 'live',
network: 'live', live: 'live', tools: 'live', media: 'live', games: 'live', community: 'live', events: 'live',
billing: 'live', codes: 'live', blog: 'live', wiki: 'live', sites: 'retired', // Sites retired 2026-10-14 (contracts 0.181.0)
billing: 'live', codes: 'live', blog: 'live', wiki: 'live', sites: 'retired', // Sites retired 2026-10-14 (contracts 0.181.0)
- news: 'live', reviews: 'live', deals: 'live', coupons: 'live', trade: 'live', host: 'live', domains: 'live', report: 'live', design: 'live', today: 'live', // News, Reviews, Coupons, Trade live since contracts 0.125.0; Domains since 0.226.0, Report 0.227.0, Design 0.228.0
+ news: 'live', reviews: 'live', deals: 'live', coupons: 'live', trade: 'live', host: 'live', domains: 'live', report: 'live', design: 'live', today: 'live', surf: 'live', // News, Reviews, Coupons, Trade live since contracts 0.125.0; Domains since 0.226.0, Report 0.227.0, Design 0.228.0
tips: 'live', vip: 'live', openre: 'live', search: 'live', sources: 'internal', chat: 'live', 'media-hub': 'live', space: 'live', // a home and redirects since the forum returned to Community (contracts 0.118.0)
tips: 'live', vip: 'live', openre: 'live', search: 'live', sources: 'internal', chat: 'live', 'media-hub': 'live', space: 'live', // a home and redirects since the forum returned to Community (contracts 0.118.0)
// tips, vip: their own sites at openvibe.tips and openvibe.vip since contracts 0.200.0 (2026-10-17)
// tips, vip: their own sites at openvibe.tips and openvibe.vip since contracts 0.200.0 (2026-10-17)
ai: 'live', // the public home and developer-app API at ai.openvibe.services since 2026-10-07 (contracts 0.110.0)
ai: 'live', // the public home and developer-app API at ai.openvibe.services since 2026-10-07 (contracts 0.110.0)
@@ -121,7 +121,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch
@@ -121,7 +121,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch
// v0.95.0 Services; Space and AI went live in v0.110.0; Food, Help and Work in v0.116.0; Quest, Rent and Watch in v0.117.0;
// v0.95.0 Services; Space and AI went live in v0.110.0; Food, Help and Work in v0.116.0; Quest, Rent and Watch in v0.117.0;
// Inventory in v0.122.0; none is retired
// Inventory in v0.122.0; none is retired
// News, Reviews, Coupons, Trade and OpenRestream in v0.125.0; Run in v0.141.0
// News, Reviews, Coupons, Trade and OpenRestream in v0.125.0; Run in v0.141.0
- assert.deepStrictEqual(st.exposure_summary, { live: 42, internal: 1, library: 4, repository: 1, placeholder: 0, retired: 1 }); // Sites retired (contracts 0.181.0); Tips and VIP live (0.200.0); Design live (0.228.0)
+ assert.deepStrictEqual(st.exposure_summary, { live: 43, internal: 1, library: 4, repository: 1, placeholder: 0, retired: 1 }); // Sites retired (contracts 0.181.0); Tips and VIP live (0.200.0); Design live (0.228.0)
// The page: no row for a service whose domain serves a placeholder shows a bare "Up".
// The page: no row for a service whose domain serves a placeholder shows a bare "Up".
const html = await fetch(base + '/status').then(x => x.text());
const html = await fetch(base + '/status').then(x => x.text());
@@ -137,7 +137,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch
@@ -137,7 +137,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch
// 3. The Frame: the nav lists only sites whose domain serves the service; the rest are soon with a reason.
// 3. The Frame: the nav lists only sites whose domain serves the service; the rest are soon with a reason.
const open = SITES.filter(s => s.status === 'open').map(s => s.id);
const open = SITES.filter(s => s.status === 'open').map(s => s.id);
- assert.deepStrictEqual(open, ['live', 'tools', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'news', 'reviews', 'tips', 'vip', 'trade', 'host', 'deals', 'coupons', 'stream', 'space', 'media-hub', 'bot', 'codes', 'actor', 'food', 'help', 'work', 'quest', 'rent', 'watch', 'inventory', 'events', 'ai', 'search', 'run', 'play', 'zone', 'domains', 'report', 'design', 'today']); // domains: open since 0.226.0, report since 0.227.0, design since 0.228.0 (the 2026-10-28 rooms); tips, vip: open since 0.200.0; zone: a site since 0.173.0; play: a site since 0.172.0; run: a site since 0.141.0; events, ai and search: sites since 0.135.0; news, reviews, trade, coupons and stream (OpenRestream): open since 0.125.0; // by site position; bot: a site since openvibe-contracts 0.106.0; host: live since 0.107.0; space: live since 0.110.0 (the forum, 2026-10-07)
+ assert.deepStrictEqual(open, ['live', 'tools', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'news', 'reviews', 'tips', 'vip', 'trade', 'host', 'deals', 'coupons', 'stream', 'space', 'media-hub', 'bot', 'codes', 'actor', 'food', 'help', 'work', 'quest', 'rent', 'watch', 'inventory', 'events', 'ai', 'search', 'run', 'play', 'zone', 'domains', 'report', 'design', 'today', 'surf']); // domains: open since 0.226.0, report since 0.227.0, design since 0.228.0 (the 2026-10-28 rooms); tips, vip: open since 0.200.0; zone: a site since 0.173.0; play: a site since 0.172.0; run: a site since 0.141.0; events, ai and search: sites since 0.135.0; news, reviews, trade, coupons and stream (OpenRestream): open since 0.125.0; // by site position; bot: a site since openvibe-contracts 0.106.0; host: live since 0.107.0; space: live since 0.110.0 (the forum, 2026-10-07)
for (const s of SITES) {
for (const s of SITES) {
assert.ok(s.service && PINNED[s.service], `${s.id} names its service`);
assert.ok(s.service && PINNED[s.service], `${s.id} names its service`);
assert.strictEqual(s.status === 'open', PINNED[s.service] === 'live', `${s.id} nav status follows exposure`);
assert.strictEqual(s.status === 'open', PINNED[s.service] === 'live', `${s.id} nav status follows exposure`);