openvibers/openvibe.sites Commits

realtime.openvibe.network: closed notice instead of a product placeholder

OP OpenVibers committed 24 Sept 2026 · 25fe50a0b1 · 23 files, +76 −83

ADR-005 (accepted 2026-09-22) closed OpenVibe.Realtime: browser realtime is part of OpenVibe.Events
(/realtime/stream, SSE with cursor resume), and Realtime's STATUS.json says stage closed. The page still
advertised "Authorized live updates, resumable" as a product to come (Host register C-82).

sites.json entries can now be a notice (kind: closed): the page says what happened and where the work
lives (links: OpenVibe.Events, ADR-005, the Realtime repository as decision record), its primary button
goes there, it is noindex with no sitemap, and no other page lists it among the domains still to open.
The status line comes from facts.json like every other page ("closed and stays as a decision record").

The vhost is unchanged: nginx keeps serving realtime.openvibe.network from dist/, now the notice.

test/build.test.js: a repository whose STATUS.json says closed must have a closed notice; notices are
noindex, have no sitemap, link every destination, are never called coming, and are not cross-listed.

Changes

ddiff --git a/build.js b/build.js
iindex a53ca71..c27ff49 100644
---- a/build.js
++++ b/build.js
@@@ -77,7 +77,7 @@ function statusText(site) {
const o = f.registry.origin;
return `${repo} is live at <a href="${esc(o)}/">${esc(o.replace(/^https?:\/\//, ''))}</a>; this subdomain is not routed to it yet, so it serves this placeholder.`;
}
-- if (k === 'closed') return `${repo} is closed, so no product launches here; this page is a placeholder.`;
++ if (k === 'closed') return `${repo} is closed and stays as a decision record; no product launches at this address.`;
return `${repo}: charter only, no code yet; this page is a placeholder.${waits}`;
}
const today = new Date().toISOString().slice(0, 10);
@@@ -92,7 +92,11 @@ function page(site) {
const title = `${site.name} — ${site.tagline}`;
const ogImage = `${NET.networkUrl}/assets/logo-512.png`;
const others = NET.liveSites.filter(s => s.url !== url.slice(0, -1));
-- const siblings = catalog.sites.filter(s => s.domain !== site.domain);
++ // A notice (site.kind: closed) is not a placeholder for a product: it says where the work went, is not
++ // indexed and is not listed among the domains still to open.
++ const notice = !!site.kind;
++ const siblings = catalog.sites.filter(s => s.domain !== site.domain && !s.kind);
++ const primary = notice && site.links && site.links[0];
const ld = {
'@context': 'https://schema.org',
'@graph': [
@@@ -111,7 +115,7 @@ function page(site) {
<title>${esc(title)}</title>
<meta name="description" content="${esc(site.description)}">
<meta name="keywords" content="${esc(site.keywords || '')}">
--<meta name="robots" content="index, follow, max-image-preview:large">
++<meta name="robots" content="${notice ? 'noindex, follow' : 'index, follow, max-image-preview:large'}">
<meta name="theme-color" content="${esc(site.accent)}">
<link rel="canonical" href="${url}">
${require('openvibe-shared/app-icon').headTags({ site: 'network' }).split('\n')[0]}
@@@ -202,14 +206,21 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<div class="tag">${esc(site.tagline)}</div>
<p class="lead">${esc(site.description)}</p>
<div class="ctas">
-- <a class="btn btn-primary" href="${NET.networkUrl}/login?return=${encodeURIComponent(url)}"><i class="fa-solid fa-right-to-bracket"></i> Sign in with OpenVibe</a>
++ ${primary ? `<a class="btn btn-primary" href="${esc(primary[1])}"><i class="fa-solid fa-arrow-right"></i> ${esc(primary[0])}</a>` : `<a class="btn btn-primary" href="${NET.networkUrl}/login?return=${encodeURIComponent(url)}"><i class="fa-solid fa-right-to-bracket"></i> Sign in with OpenVibe</a>`}
<a class="btn" href="${NET.networkUrl}/#network"><i class="fa-solid fa-circle-nodes"></i> The whole network</a>
<a class="btn" href="${esc(NET.discord)}" rel="noopener"><i class="fa-brands fa-discord"></i> Follow the build</a>
</div>
<p class="status"><i class="fa-solid fa-clock" aria-hidden="true"></i> Status: <b>${esc(STANDING[standing(site)].label)}</b> · ${statusText(site)} · as of ${esc(facts.generated)} · <a href="/status.json">status.json</a></p>
</header>
-- <section id="what">
++${notice ? ` <section id="what" class="meanwhile">
++ <h2>Where to go</h2>
++ <p class="lead2">${esc(site.linksLead || 'What this address used to stand for lives here now.')}</p>
++ <div class="grid">
++ ${site.links.map(([h, href, d]) => `<a class="card" href="${esc(href)}"><div class="ic"><i class="fa-solid fa-arrow-up-right-from-square" aria-hidden="true"></i></div><b>${esc(h)}</b><span>${esc(d)}</span><span class="go">${esc(href.replace(/^https?:\/\//, '').replace(/\/$/, ''))} →</span></a>`).join('\n ')}
++ </div>
++ </section>
++` : ` <section id="what">
<h2>What ${esc(site.name)} will be</h2>
<p class="lead2">Same account, same themes, same navbar as every other OpenVibe site — built in the open, run by its community.</p>
<div class="grid">
@@@ -224,7 +235,7 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
${(site.meanwhile || []).map(([h, href, d]) => `<a class="card" href="${esc(href)}"><div class="ic"><i class="fa-solid fa-arrow-up-right-from-square" aria-hidden="true"></i></div><b>${esc(h)}</b><span>${esc(d)}</span><span class="go">${esc(href.replace(/^https?:\/\//, '').replace(/\/$/, ''))} →</span></a>`).join('\n ')}
</div>
</section>
--
++`}
<section class="net">
<h2>The OpenVibe network</h2>
<p class="lead2">One account for all of it. Sign in once and every site signs you in.</p>
@@@ -250,7 +261,7 @@ ${require('openvibe-shared/footer').ssr({ service: 'network', variant: 'full' })
<script src="${NET.networkUrl}/shared/footer.js"></script>
<script>
(function () {
-- if (window.OpenVibeNavbar) { try { OpenVibeNavbar.init({ service: '${esc(site.tld)}', apiBase: '${NET.networkUrl}', links: [{ label: 'What it will be', href: '#what' }, { label: 'The network', href: '${NET.networkUrl}/#network', external: false }], history: { type: 'page', title: '${esc(site.name)}' } }); } catch (e) {} }
++ if (window.OpenVibeNavbar) { try { OpenVibeNavbar.init({ service: '${esc(site.tld)}', apiBase: '${NET.networkUrl}', links: [{ label: '${notice ? 'Where to go' : 'What it will be'}', href: '#what' }, { label: 'The network', href: '${NET.networkUrl}/#network', external: false }], history: { type: 'page', title: '${esc(site.name)}' } }); } catch (e) {} }
if (window.OpenVibeFooter) { try { OpenVibeFooter.init({ service: '${esc(site.tld)}', variant: 'full', links: [{ heading: '${esc(site.name)}', items: [{ name: 'Sign in', url: '${NET.networkUrl}/login?return=${encodeURIComponent(url)}' }, { name: 'The network', url: '${NET.networkUrl}/#network' }, { name: 'Discord', url: '${esc(NET.discord)}' }] }] }); } catch (e) {} }
})();
</script>
@@@ -259,7 +270,8 @@ ${require('openvibe-shared/footer').ssr({ service: 'network', variant: 'full' })
`;
}
--function robots(site) { return `User-agent: *\nAllow: /\nDisallow: /auth/\nSitemap: https://${site.domain}/sitemap.xml\n`; }
++// A notice page is noindex and has no sitemap; a placeholder lists its one page.
++function robots(site) { return `User-agent: *\nAllow: /\nDisallow: /auth/\n${site.kind ? '' : `Sitemap: https://${site.domain}/sitemap.xml\n`}`; }
function sitemap(site) { return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n <url><loc>https://${site.domain}/</loc><lastmod>${today}</lastmod><changefreq>weekly</changefreq><priority>1.0</priority></url>\n</urlset>\n`; }
function manifest(site) { return JSON.stringify(require('openvibe-shared/app-icon').manifest({ site: 'network', name: site.name, shortName: site.name.split('.').pop(), description: site.tagline, iconBase: `${NET.networkUrl}/assets` }), null, 2) + '\n'; }
@@@ -349,7 +361,7 @@ function build() {
for (const site of catalog.sites) {
out[`${site.domain}/index.html`] = page(site);
out[`${site.domain}/robots.txt`] = robots(site);
-- out[`${site.domain}/sitemap.xml`] = sitemap(site);
++ if (!site.kind) out[`${site.domain}/sitemap.xml`] = sitemap(site);
out[`${site.domain}/manifest.webmanifest`] = manifest(site);
out[`${site.domain}/status.json`] = statusJson(site);
for (const kind of ['terms', 'privacy', 'dmca']) out[`${site.domain}/${kind}.html`] = legal.page(kind, legalSite(site));
ddiff --git a/dist/admin.openvibe.network/index.html b/dist/admin.openvibe.network/index.html
iindex b45add1..9a4906f 100644
---- a/dist/admin.openvibe.network/index.html
++++ b/dist/admin.openvibe.network/index.html
@@@ -149,7 +149,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://auth.openvibe.network/"><i class="fa-solid fa-key" aria-hidden="true"></i>auth.openvibe.network</a>
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/ai.openvibe.network/index.html b/dist/ai.openvibe.network/index.html
iindex 7059583..59b0dff 100644
---- a/dist/ai.openvibe.network/index.html
++++ b/dist/ai.openvibe.network/index.html
@@@ -150,7 +150,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/api.openvibe.network/index.html b/dist/api.openvibe.network/index.html
iindex b58b580..ff18311 100644
---- a/dist/api.openvibe.network/index.html
++++ b/dist/api.openvibe.network/index.html
@@@ -151,7 +151,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://auth.openvibe.network/"><i class="fa-solid fa-key" aria-hidden="true"></i>auth.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/auth.openvibe.network/index.html b/dist/auth.openvibe.network/index.html
iindex 2b2daed..867457e 100644
---- a/dist/auth.openvibe.network/index.html
++++ b/dist/auth.openvibe.network/index.html
@@@ -150,7 +150,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/openre.stream/index.html b/dist/openre.stream/index.html
iindex a797a4f..d16dd13 100644
---- a/dist/openre.stream/index.html
++++ b/dist/openre.stream/index.html
@@@ -150,7 +150,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/openvibe.chat/index.html b/dist/openvibe.chat/index.html
iindex dd41d60..881ed96 100644
---- a/dist/openvibe.chat/index.html
++++ b/dist/openvibe.chat/index.html
@@@ -150,7 +150,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/openvibe.coupons/index.html b/dist/openvibe.coupons/index.html
iindex 8868128..d3eb2f4 100644
---- a/dist/openvibe.coupons/index.html
++++ b/dist/openvibe.coupons/index.html
@@@ -149,7 +149,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/openvibe.deals/index.html b/dist/openvibe.deals/index.html
iindex a2c046b..7e5d395 100644
---- a/dist/openvibe.deals/index.html
++++ b/dist/openvibe.deals/index.html
@@@ -149,7 +149,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/openvibe.host/index.html b/dist/openvibe.host/index.html
iindex a933ffb..e13e556 100644
---- a/dist/openvibe.host/index.html
++++ b/dist/openvibe.host/index.html
@@@ -150,7 +150,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/openvibe.news/index.html b/dist/openvibe.news/index.html
iindex 2e0b958..7b1e7e6 100644
---- a/dist/openvibe.news/index.html
++++ b/dist/openvibe.news/index.html
@@@ -149,7 +149,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/openvibe.reviews/index.html b/dist/openvibe.reviews/index.html
iindex d1d5888..07803d4 100644
---- a/dist/openvibe.reviews/index.html
++++ b/dist/openvibe.reviews/index.html
@@@ -149,7 +149,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/openvibe.tips/index.html b/dist/openvibe.tips/index.html
iindex d505529..41ef22e 100644
---- a/dist/openvibe.tips/index.html
++++ b/dist/openvibe.tips/index.html
@@@ -149,7 +149,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/openvibe.trade/index.html b/dist/openvibe.trade/index.html
iindex 186669c..d6a6312 100644
---- a/dist/openvibe.trade/index.html
++++ b/dist/openvibe.trade/index.html
@@@ -149,7 +149,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/openvibe.vip/index.html b/dist/openvibe.vip/index.html
iindex 9a65c0f..0e831a4 100644
---- a/dist/openvibe.vip/index.html
++++ b/dist/openvibe.vip/index.html
@@@ -149,7 +149,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
<a href="https://themes.openvibe.network/"><i class="fa-solid fa-palette" aria-hidden="true"></i>themes.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/dist/realtime.openvibe.network/index.html b/dist/realtime.openvibe.network/index.html
iindex 7b8d323..d0c13ef 100644
---- a/dist/realtime.openvibe.network/index.html
++++ b/dist/realtime.openvibe.network/index.html
@@@ -3,10 +3,10 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
--<title>realtime.openvibe.network — Authorized live updates, resumable.</title>
--<meta name="description" content="realtime.openvibe.network will be the browser delivery plane for events: WebSocket and SSE subscriptions with cursors, presence and gap recovery, separate from the durable event store.">
--<meta name="keywords" content="openvibe realtime, openvibe network realtime, openvibe platform">
--<meta name="robots" content="index, follow, max-image-preview:large">
++<title>realtime.openvibe.network — Closed: realtime delivery is part of OpenVibe.Events.</title>
++<meta name="description" content="OpenVibe.Realtime was planned as a separate service for live updates in the browser. Decision record ADR-005 (accepted 2026-09-22) closed it: browser realtime is part of OpenVibe.Events, as server-sent events with cursor resume at events.openvibe.network/realtime/stream. This address serves only this notice.">
++<meta name="keywords" content="openvibe realtime, openvibe events, server-sent events">
++<meta name="robots" content="noindex, follow">
<meta name="theme-color" content="#34d399">
<link rel="canonical" href="https://realtime.openvibe.network/">
<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
@@@ -16,15 +16,15 @@
<meta property="og:type" content="website">
<meta property="og:site_name" content="realtime.openvibe.network">
<meta property="og:url" content="https://realtime.openvibe.network/">
--<meta property="og:title" content="realtime.openvibe.network — Authorized live updates, resumable.">
--<meta property="og:description" content="realtime.openvibe.network will be the browser delivery plane for events: WebSocket and SSE subscriptions with cursors, presence and gap recovery, separate from the durable event store.">
++<meta property="og:title" content="realtime.openvibe.network — Closed: realtime delivery is part of OpenVibe.Events.">
++<meta property="og:description" content="OpenVibe.Realtime was planned as a separate service for live updates in the browser. Decision record ADR-005 (accepted 2026-09-22) closed it: browser realtime is part of OpenVibe.Events, as server-sent events with cursor resume at events.openvibe.network/realtime/stream. This address serves only this notice.">
<meta property="og:image" content="https://openvibe.network/assets/logo-512.png">
<meta property="og:image:alt" content="OpenVibe">
<meta name="twitter:card" content="summary">
--<meta name="twitter:title" content="realtime.openvibe.network — Authorized live updates, resumable.">
--<meta name="twitter:description" content="realtime.openvibe.network will be the browser delivery plane for events: WebSocket and SSE subscriptions with cursors, presence and gap recovery, separate from the durable event store.">
++<meta name="twitter:title" content="realtime.openvibe.network — Closed: realtime delivery is part of OpenVibe.Events.">
++<meta name="twitter:description" content="OpenVibe.Realtime was planned as a separate service for live updates in the browser. Decision record ADR-005 (accepted 2026-09-22) closed it: browser realtime is part of OpenVibe.Events, as server-sent events with cursor resume at events.openvibe.network/realtime/stream. This address serves only this notice.">
<meta name="twitter:image" content="https://openvibe.network/assets/logo-512.png">
--<script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"WebSite","@id":"https://realtime.openvibe.network/#site","url":"https://realtime.openvibe.network/","name":"realtime.openvibe.network","description":"realtime.openvibe.network will be the browser delivery plane for events: WebSocket and SSE subscriptions with cursors, presence and gap recovery, separate from the durable event store.","inLanguage":"en","publisher":{"@type":"Organization","@id":"https://openvibe.network/#org","name":"OpenVibe","url":"https://openvibe.network/","logo":"https://openvibe.network/assets/logo-512.png","sameAs":["https://github.com/OpenVibers","https://discord.gg/M6MuRUaeJj"]},"isPartOf":{"@type":"WebSite","@id":"https://openvibe.network/#site","name":"OpenVibe.Network","url":"https://openvibe.network/"}},{"@type":"WebPage","@id":"https://realtime.openvibe.network/#page","url":"https://realtime.openvibe.network/","name":"realtime.openvibe.network — Authorized live updates, resumable.","description":"realtime.openvibe.network will be the browser delivery plane for events: WebSocket and SSE subscriptions with cursors, presence and gap recovery, separate from the durable event store.","isPartOf":{"@id":"https://realtime.openvibe.network/#site"},"datePublished":"2026-09-17","dateModified":"2026-09-24","breadcrumb":{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"OpenVibe","item":"https://openvibe.network/"},{"@type":"ListItem","position":2,"name":"realtime.openvibe.network","item":"https://realtime.openvibe.network/"}]}}]}</script>
++<script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"WebSite","@id":"https://realtime.openvibe.network/#site","url":"https://realtime.openvibe.network/","name":"realtime.openvibe.network","description":"OpenVibe.Realtime was planned as a separate service for live updates in the browser. Decision record ADR-005 (accepted 2026-09-22) closed it: browser realtime is part of OpenVibe.Events, as server-sent events with cursor resume at events.openvibe.network/realtime/stream. This address serves only this notice.","inLanguage":"en","publisher":{"@type":"Organization","@id":"https://openvibe.network/#org","name":"OpenVibe","url":"https://openvibe.network/","logo":"https://openvibe.network/assets/logo-512.png","sameAs":["https://github.com/OpenVibers","https://discord.gg/M6MuRUaeJj"]},"isPartOf":{"@type":"WebSite","@id":"https://openvibe.network/#site","name":"OpenVibe.Network","url":"https://openvibe.network/"}},{"@type":"WebPage","@id":"https://realtime.openvibe.network/#page","url":"https://realtime.openvibe.network/","name":"realtime.openvibe.network — Closed: realtime delivery is part of OpenVibe.Events.","description":"OpenVibe.Realtime was planned as a separate service for live updates in the browser. Decision record ADR-005 (accepted 2026-09-22) closed it: browser realtime is part of OpenVibe.Events, as server-sent events with cursor resume at events.openvibe.network/realtime/stream. This address serves only this notice.","isPartOf":{"@id":"https://realtime.openvibe.network/#site"},"datePublished":"2026-09-17","dateModified":"2026-09-24","breadcrumb":{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"OpenVibe","item":"https://openvibe.network/"},{"@type":"ListItem","position":2,"name":"realtime.openvibe.network","item":"https://realtime.openvibe.network/"}]}}]}</script>
<script>(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();</script>
<script src="https://openvibe.network/shared/theme-loader.js" defer></script>
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
@@@ -94,32 +94,23 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<div class="mark"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i></div>
<div class="badge"><span class="ov-mark" data-size="16" data-variant="realtime"></span> Part of the OpenVibe network · <b>closed</b></div>
<h1><span class="d">realtime</span><span class="tld">.openvibe.network</span></h1>
-- <div class="tag">Authorized live updates, resumable.</div>
-- <p class="lead">realtime.openvibe.network will be the browser delivery plane for events: WebSocket and SSE subscriptions with cursors, presence and gap recovery, separate from the durable event store.</p>
++ <div class="tag">Closed: realtime delivery is part of OpenVibe.Events.</div>
++ <p class="lead">OpenVibe.Realtime was planned as a separate service for live updates in the browser. Decision record ADR-005 (accepted 2026-09-22) closed it: browser realtime is part of OpenVibe.Events, as server-sent events with cursor resume at events.openvibe.network/realtime/stream. This address serves only this notice.</p>
<div class="ctas">
-- <a class="btn btn-primary" href="https://openvibe.network/login?return=https%3A%2F%2Frealtime.openvibe.network%2F"><i class="fa-solid fa-right-to-bracket"></i> Sign in with OpenVibe</a>
++ <a class="btn btn-primary" href="https://events.openvibe.network/"><i class="fa-solid fa-arrow-right"></i> OpenVibe.Events</a>
<a class="btn" href="https://openvibe.network/#network"><i class="fa-solid fa-circle-nodes"></i> The whole network</a>
<a class="btn" href="https://discord.gg/M6MuRUaeJj" rel="noopener"><i class="fa-brands fa-discord"></i> Follow the build</a>
</div>
-- <p class="status"><i class="fa-solid fa-clock" aria-hidden="true"></i> Status: <b>closed</b> · <a href="https://github.com/OpenVibers/OpenVibe.Realtime" rel="noopener"><code>OpenVibers/OpenVibe.Realtime</code></a> is closed, so no product launches here; this page is a placeholder. · as of 2026-09-24 · <a href="/status.json">status.json</a></p>
++ <p class="status"><i class="fa-solid fa-clock" aria-hidden="true"></i> Status: <b>closed</b> · <a href="https://github.com/OpenVibers/OpenVibe.Realtime" rel="noopener"><code>OpenVibers/OpenVibe.Realtime</code></a> is closed and stays as a decision record; no product launches at this address. · as of 2026-09-24 · <a href="/status.json">status.json</a></p>
</header>
-- <section id="what">
-- <h2>What realtime.openvibe.network will be</h2>
-- <p class="lead2">Same account, same themes, same navbar as every other OpenVibe site — built in the open, run by its community.</p>
++ <section id="what" class="meanwhile">
++ <h2>Where to go</h2>
++ <p class="lead2">Realtime delivery for browsers is a part of OpenVibe.Events; the decision and its record are public.</p>
<div class="grid">
-- <div class="card"><div class="ic"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i></div><b>Topics</b><span>Subscribe to exactly the streams your account may see.</span></div>
-- <div class="card"><div class="ic"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i></div><b>Resume</b><span>Reconnect from a cursor and get what you missed.</span></div>
-- <div class="card"><div class="ic"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i></div><b>Presence</b><span>Who is here, without polling.</span></div>
-- <div class="card"><div class="ic"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i></div><b>Not media</b><span>Video and audio keep their own transports.</span></div>
-- </div>
-- </section>
--
-- <section class="meanwhile">
-- <h2>Meanwhile, on the network</h2>
-- <p class="lead2">Most of this already exists somewhere on OpenVibe — start there today.</p>
-- <div class="grid">
-- <a class="card" href="https://openvibe.live"><div class="ic"><i class="fa-solid fa-arrow-up-right-from-square" aria-hidden="true"></i></div><b>Chat and stream updates</b><span>Realtime today runs inside OpenVibe.Live.</span><span class="go">openvibe.live →</span></a>
++ <a class="card" href="https://events.openvibe.network/"><div class="ic"><i class="fa-solid fa-arrow-up-right-from-square" aria-hidden="true"></i></div><b>OpenVibe.Events</b><span>Durable events, subscriptions and signed delivery; browsers subscribe at /realtime/stream.</span><span class="go">events.openvibe.network →</span></a>
++ <a class="card" href="https://github.com/OpenVibers/OpenVibe.Contracts/blob/main/docs/adr/ADR-005-realtime.md"><div class="ic"><i class="fa-solid fa-arrow-up-right-from-square" aria-hidden="true"></i></div><b>ADR-005: realtime</b><span>The decision, and when a separate realtime service would be reconsidered.</span><span class="go">github.com/OpenVibers/OpenVibe.Contracts/blob/main/docs/adr/ADR-005-realtime.md →</span></a>
++ <a class="card" href="https://github.com/OpenVibers/OpenVibe.Realtime"><div class="ic"><i class="fa-solid fa-arrow-up-right-from-square" aria-hidden="true"></i></div><b>OpenVibe.Realtime</b><span>The repository stays as the decision record.</span><span class="go">github.com/OpenVibers/OpenVibe.Realtime →</span></a>
</div>
</section>
@@@ -253,7 +244,7 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<script src="https://openvibe.network/shared/footer.js"></script>
<script>
(function () {
-- if (window.OpenVibeNavbar) { try { OpenVibeNavbar.init({ service: 'realtime', apiBase: 'https://openvibe.network', links: [{ label: 'What it will be', href: '#what' }, { label: 'The network', href: 'https://openvibe.network/#network', external: false }], history: { type: 'page', title: 'realtime.openvibe.network' } }); } catch (e) {} }
++ if (window.OpenVibeNavbar) { try { OpenVibeNavbar.init({ service: 'realtime', apiBase: 'https://openvibe.network', links: [{ label: 'Where to go', href: '#what' }, { label: 'The network', href: 'https://openvibe.network/#network', external: false }], history: { type: 'page', title: 'realtime.openvibe.network' } }); } catch (e) {} }
if (window.OpenVibeFooter) { try { OpenVibeFooter.init({ service: 'realtime', variant: 'full', links: [{ heading: 'realtime.openvibe.network', items: [{ name: 'Sign in', url: 'https://openvibe.network/login?return=https%3A%2F%2Frealtime.openvibe.network%2F' }, { name: 'The network', url: 'https://openvibe.network/#network' }, { name: 'Discord', url: 'https://discord.gg/M6MuRUaeJj' }] }] }); } catch (e) {} }
})();
</script>
ddiff --git a/dist/realtime.openvibe.network/manifest.webmanifest b/dist/realtime.openvibe.network/manifest.webmanifest
iindex b2b3d16..3b4457e 100644
---- a/dist/realtime.openvibe.network/manifest.webmanifest
++++ b/dist/realtime.openvibe.network/manifest.webmanifest
@@@ -1,7 +1,7 @@
{
"name": "realtime.openvibe.network",
"short_name": "network",
-- "description": "Authorized live updates, resumable.",
++ "description": "Closed: realtime delivery is part of OpenVibe.Events.",
"start_url": "/",
"scope": "/",
"display": "standalone",
ddiff --git a/dist/realtime.openvibe.network/robots.txt b/dist/realtime.openvibe.network/robots.txt
iindex 79f0f22..07b86e6 100644
---- a/dist/realtime.openvibe.network/robots.txt
++++ b/dist/realtime.openvibe.network/robots.txt
@@@ -1,4 +1,3 @@
User-agent: *
Allow: /
Disallow: /auth/
--Sitemap: https://realtime.openvibe.network/sitemap.xml
ddiff --git a/dist/realtime.openvibe.network/sitemap.xml b/dist/realtime.openvibe.network/sitemap.xml
ddeleted file mode 100644
iindex b83bd54..0000000
---- a/dist/realtime.openvibe.network/sitemap.xml
++++ /dev/null
@@@ -1,4 +0,0 @@
--<?xml version="1.0" encoding="UTF-8"?>
--<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
-- <url><loc>https://realtime.openvibe.network/</loc><lastmod>2026-09-24</lastmod><changefreq>weekly</changefreq><priority>1.0</priority></url>
--</urlset>
ddiff --git a/dist/realtime.openvibe.network/status.json b/dist/realtime.openvibe.network/status.json
iindex 7ff809d..7584528 100644
---- a/dist/realtime.openvibe.network/status.json
++++ b/dist/realtime.openvibe.network/status.json
@@@ -1,7 +1,7 @@
{
"domain": "realtime.openvibe.network",
"name": "realtime.openvibe.network",
-- "stage": "placeholder",
++ "stage": "closed",
"live": false,
"plannedRepo": "OpenVibers/OpenVibe.Realtime",
"repoUrl": "https://github.com/OpenVibers/OpenVibe.Realtime",
ddiff --git a/dist/themes.openvibe.network/index.html b/dist/themes.openvibe.network/index.html
iindex 983453e..25a5cd8 100644
---- a/dist/themes.openvibe.network/index.html
++++ b/dist/themes.openvibe.network/index.html
@@@ -149,7 +149,6 @@ a.card:hover{transform:translateY(-3px);border-color:rgba(var(--site-rgb),.6);bo
<a href="https://auth.openvibe.network/"><i class="fa-solid fa-key" aria-hidden="true"></i>auth.openvibe.network</a>
<a href="https://api.openvibe.network/"><i class="fa-solid fa-plug" aria-hidden="true"></i>api.openvibe.network</a>
<a href="https://admin.openvibe.network/"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i>admin.openvibe.network</a>
-- <a href="https://realtime.openvibe.network/"><i class="fa-solid fa-satellite-dish" aria-hidden="true"></i>realtime.openvibe.network</a>
<a href="https://ai.openvibe.network/"><i class="fa-solid fa-robot" aria-hidden="true"></i>ai.openvibe.network</a>
</div>
</section>
ddiff --git a/sites.json b/sites.json
iindex d34dbc0..0f21a2c 100644
---- a/sites.json
++++ b/sites.json
@@@ -627,39 +627,33 @@
"openvibe.network"
],
"tld": "realtime",
++ "kind": "closed",
"name": "realtime.openvibe.network",
"icon": "fa-satellite-dish",
"accent": "#34d399",
"plannedRepo": "OpenVibe.Realtime",
"legalProfile": "info",
-- "tagline": "Authorized live updates, resumable.",
-- "description": "realtime.openvibe.network will be the browser delivery plane for events: WebSocket and SSE subscriptions with cursors, presence and gap recovery, separate from the durable event store.",
-- "pillars": [
++ "tagline": "Closed: realtime delivery is part of OpenVibe.Events.",
++ "description": "OpenVibe.Realtime was planned as a separate service for live updates in the browser. Decision record ADR-005 (accepted 2026-09-22) closed it: browser realtime is part of OpenVibe.Events, as server-sent events with cursor resume at events.openvibe.network/realtime/stream. This address serves only this notice.",
++ "linksLead": "Realtime delivery for browsers is a part of OpenVibe.Events; the decision and its record are public.",
++ "links": [
[
-- "Topics",
-- "Subscribe to exactly the streams your account may see."
++ "OpenVibe.Events",
++ "https://events.openvibe.network/",
++ "Durable events, subscriptions and signed delivery; browsers subscribe at /realtime/stream."
],
[
-- "Resume",
-- "Reconnect from a cursor and get what you missed."
++ "ADR-005: realtime",
++ "https://github.com/OpenVibers/OpenVibe.Contracts/blob/main/docs/adr/ADR-005-realtime.md",
++ "The decision, and when a separate realtime service would be reconsidered."
],
[
-- "Presence",
-- "Who is here, without polling."
-- ],
-- [
-- "Not media",
-- "Video and audio keep their own transports."
-- ]
-- ],
-- "meanwhile": [
-- [
-- "Chat and stream updates",
-- "https://openvibe.live",
-- "Realtime today runs inside OpenVibe.Live."
++ "OpenVibe.Realtime",
++ "https://github.com/OpenVibers/OpenVibe.Realtime",
++ "The repository stays as the decision record."
]
],
-- "keywords": "openvibe realtime, openvibe network realtime, openvibe platform"
++ "keywords": "openvibe realtime, openvibe events, server-sent events"
},
{
"domain": "ai.openvibe.network",
ddiff --git a/test/build.test.js b/test/build.test.js
iindex 28f6d4b..165b54f 100644
---- a/test/build.test.js
++++ b/test/build.test.js
@@@ -26,15 +26,30 @@ for (const site of catalog.sites) {
pages++;
assert.match(html, /<title>[^<]+<\/title>/, `${d}: title`);
assert.match(html, new RegExp(`<link rel="canonical" href="https://${d.replace(/\./g, '\\.')}/?"`), `${d}: canonical`);
-- assert.match(html, /placeholder/i, `${d}: says it is a placeholder (never claim a product is live)`);
++ if (!site.kind) assert.match(html, /placeholder/i, `${d}: says it is a placeholder (never claim a product is live)`);
assert.doesNotMatch(html, PRICING, `${d}: pricing copy`);
assert.doesNotMatch(html, UNBACKED, `${d}: claims a hosting product that does not exist`);
for (const m of html.matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g)) {
assert.doesNotThrow(() => JSON.parse(m[1]), `${d}: JSON-LD parses`);
}
-- for (const f of ['robots.txt', 'sitemap.xml', 'manifest.webmanifest']) {
++ for (const f of ['robots.txt', 'manifest.webmanifest', 'status.json', ...(site.kind ? [] : ['sitemap.xml'])]) {
assert.ok(fs.existsSync(path.join(ROOT, 'dist', d, f)), `${d}: ${f}`);
}
++ if (site.kind) {
++ // A notice (a closed product, a pointer) is not indexed, has no sitemap and is not listed as opening.
++ assert.match(html, /<meta name="robots" content="noindex, follow">/, `${d}: notice is noindex`);
++ assert.ok(!fs.existsSync(path.join(ROOT, 'dist', d, 'sitemap.xml')), `${d}: notice has no sitemap`);
++ assert.doesNotMatch(read(`dist/${d}/robots.txt`), /Sitemap:/, `${d}: robots.txt names no sitemap`);
++ assert.ok(Array.isArray(site.links) && site.links.length, `${d}: notice links where to go`);
++ for (const [, href] of site.links) assert.ok(html.includes(`href="${href}"`), `${d}: links ${href}`);
++ for (const other of catalog.sites) {
++ if (other.domain !== d) assert.ok(!read(`dist/${other.domain}/index.html`).includes(`href="https://${d}/"`), `${other.domain}: does not list the ${site.kind} ${d} as a domain still to open`);
++ }
++ }
++ if (site.kind === 'closed') {
++ assert.match(html, /Status: <b>closed<\/b>/, `${d}: says closed`);
++ assert.doesNotMatch(html, /will be|opening soon|coming soon/i, `${d}: a closed product is never described as coming`);
++ }
const conf = read(`deploy/nginx/${d}.conf`);
assert.ok(conf.includes(`server_name ${d}`), `${d}: vhost server_name`);
assert.ok(conf.includes(`root /opt/openvibe.sites/dist/${d};`), `${d}: vhost root`);
@@@ -53,6 +68,7 @@ for (const site of catalog.sites) {
const html = read(`dist/${d}/index.html`);
const status = JSON.parse(read(`dist/${d}/status.json`));
const st = f.status || {}; const rg = f.registry || {};
++ if (st.stage === 'closed') assert.strictEqual(site.kind, 'closed', `${d}: ${site.plannedRepo} is closed (STATUS.json), so its page is a closed notice, not a placeholder`);
const hasCode = st.code === true || ['live', 'internal'].includes(rg.state);
if (hasCode) {
assert.doesNotMatch(html, NO_CODE, `${d}: ${site.plannedRepo} has code, so the page never says "charter only, no code yet"`);