openvibers/openvibe.network Commits
OpenVibe.Design is live: the design OAuth client signs people in at openvibe.design, the domain leaves the parked list, contracts v0.228.0 (was v0.227.0) (#391)
Changes
@@@ -283,24 +283,3 @@ server {return 302 https://openvibe.live/broadcast;}--# openvibe.design → the network's themes--server {-- listen 80;-- listen [::]:80;-- server_name openvibe.design www.openvibe.design;-- return 302 https://openvibe.network/themes;--}----server {-- listen 443 ssl;-- listen [::]:443 ssl;-- http2 on;-- server_name openvibe.design www.openvibe.design;-- ssl_certificate /etc/letsencrypt/live/openvibe.design/fullchain.pem;-- ssl_certificate_key /etc/letsencrypt/live/openvibe.design/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.network/themes;--}@@@ -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.227.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.228.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.29.0","pg": "^8.23.0",@@@ -1320,9 +1320,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.227.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.227.0",-- "integrity": "sha512-5B3PUpcN2+WGq/8tEai+fpTznza8PcPTTYKkmhkx8Vpn/nRuQV1DupZwAciSzGbZufgdaNI/28b4AVLtylwrAA==",++ "version": "0.228.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.228.0",++ "integrity": "sha512-U80yv+sf5oQGYGxsx74Gs+Q0T9UE0IykupBDxjGcub2DnHFLJQgfOHy56DuxDhjScKJI8B/nKT+QMAB4JfbsvQ==","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.227.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.228.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.29.0","pg": "^8.23.0",@@@ -120,6 +120,8 @@ async function seedDb(db, { log = console } = {}) {// Food near you, meal plans and a pantry; the help centre and its tickets; job listings and saved searches (plan T19).{ client_id: 'food', name: 'OpenVibe.Food', redirect_uris: ['https://openvibe.food/auth/callback'] },{ client_id: 'help', name: 'OpenVibe.Help', redirect_uris: ['https://openvibe.help/auth/callback'] },++ // The theme studio (plan 2026-10-28 §5.2); the client is created first as Design's principal.++ { client_id: 'design', name: 'OpenVibe.Design', redirect_uris: ['https://openvibe.design/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'] },@@@ -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'], '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'], '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']]++[['games', 'games'], ['quest', 'games'], ['inventory', 'people'], ['watch', 'build'], ['download', 'streams'], ['search', 'answers'], ['chat', 'people'], ['domains', 'make'], ['report', 'answers'], ['design', 'make']].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, 42); // Domains since contracts 0.226.0, Report since 0.227.0++assert.strictEqual(p.nav.length, 43); // Domains since contracts 0.226.0, Report since 0.227.0, Design since 0.228.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', // News, Reviews, Coupons, Trade live since contracts 0.125.0; Domains since 0.226.0++ news: 'live', reviews: 'live', deals: 'live', coupons: 'live', trade: 'live', host: 'live', domains: 'live', report: 'live', design: '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: 40, internal: 1, library: 4, repository: 1, placeholder: 0, retired: 1 }); // Sites retired (contracts 0.181.0); Tips and VIP live (0.200.0)++ assert.deepStrictEqual(st.exposure_summary, { live: 41, 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']); // domains: open since 0.226.0, report since 0.227.0 (the first 2026-10-28 room); 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']); // 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`);
@@ -283,24 +283,3 @@ server {
@@ -283,24 +283,3 @@ server {
return 302 https://openvibe.live/broadcast;
return 302 https://openvibe.live/broadcast;
}
}
-# openvibe.design → the network's themes
-server {
- listen 80;
- listen [::]:80;
- server_name openvibe.design www.openvibe.design;
- return 302 https://openvibe.network/themes;
-}
-
-server {
- listen 443 ssl;
- listen [::]:443 ssl;
- http2 on;
- server_name openvibe.design www.openvibe.design;
- ssl_certificate /etc/letsencrypt/live/openvibe.design/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/openvibe.design/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.network/themes;
-}
@@ -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.227.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.228.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.29.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.29.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.227.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.227.0",
- "integrity": "sha512-5B3PUpcN2+WGq/8tEai+fpTznza8PcPTTYKkmhkx8Vpn/nRuQV1DupZwAciSzGbZufgdaNI/28b4AVLtylwrAA==",
+ "version": "0.228.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.228.0",
+ "integrity": "sha512-U80yv+sf5oQGYGxsx74Gs+Q0T9UE0IykupBDxjGcub2DnHFLJQgfOHy56DuxDhjScKJI8B/nKT+QMAB4JfbsvQ==",
"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.227.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.228.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.29.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.29.0",
"pg": "^8.23.0",
"pg": "^8.23.0",
@@ -120,6 +120,8 @@ async function seedDb(db, { log = console } = {}) {
@@ -120,6 +120,8 @@ async function seedDb(db, { log = console } = {}) {
// Food near you, meal plans and a pantry; the help centre and its tickets; job listings and saved searches (plan T19).
// Food near you, meal plans and a pantry; the help centre and its tickets; job listings and saved searches (plan T19).
{ client_id: 'food', name: 'OpenVibe.Food', redirect_uris: ['https://openvibe.food/auth/callback'] },
{ client_id: 'food', name: 'OpenVibe.Food', redirect_uris: ['https://openvibe.food/auth/callback'] },
{ client_id: 'help', name: 'OpenVibe.Help', redirect_uris: ['https://openvibe.help/auth/callback'] },
{ client_id: 'help', name: 'OpenVibe.Help', redirect_uris: ['https://openvibe.help/auth/callback'] },
+ // The theme studio (plan 2026-10-28 §5.2); the client is created first as Design's principal.
+ { client_id: 'design', name: 'OpenVibe.Design', redirect_uris: ['https://openvibe.design/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'] },
@@ -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'], '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'], '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']]
+[['games', 'games'], ['quest', 'games'], ['inventory', 'people'], ['watch', 'build'], ['download', 'streams'], ['search', 'answers'], ['chat', 'people'], ['domains', 'make'], ['report', 'answers'], ['design', 'make']]
.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, 42); // Domains since contracts 0.226.0, Report since 0.227.0
+assert.strictEqual(p.nav.length, 43); // Domains since contracts 0.226.0, Report since 0.227.0, Design since 0.228.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', // News, Reviews, Coupons, Trade live since contracts 0.125.0; Domains since 0.226.0
+ news: 'live', reviews: 'live', deals: 'live', coupons: 'live', trade: 'live', host: 'live', domains: 'live', report: 'live', design: '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: 40, internal: 1, library: 4, repository: 1, placeholder: 0, retired: 1 }); // Sites retired (contracts 0.181.0); Tips and VIP live (0.200.0)
+ assert.deepStrictEqual(st.exposure_summary, { live: 41, 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']); // domains: open since 0.226.0, report since 0.227.0 (the first 2026-10-28 room); 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']); // 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`);