openvibers/openvibe.live Commits

SPA repair: fix rename-broken identifiers, script refs (vibes/opencoins), OpenVibe.Live branding; proxy storage tier admin to Media

OP OpenVibers committed 17 Aug 2026 · dccf79f659 · 30 files, +170 −177


Changes

ddiff --git a/hardware/controller.py b/hardware/controller.py
iindex 744e6832..d816f4a4 100644
---- a/hardware/controller.py
++++ b/hardware/controller.py
@@@ -144,7 +144,7 @@ class HardwareController:
# TTS
def cmd_tts(self, data=None):
-- text = (data or {}).get("text", "hello from openvibe streamer")
++ text = (data or {}).get("text", "hello from OpenVibe.Live")
try:
subprocess.Popen(["espeak", "-s", "150", text],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
ddiff --git a/public/dmca.html b/public/dmca.html
iindex 7029db19..71eaf11a 100644
---- a/public/dmca.html
++++ b/public/dmca.html
@@@ -201,7 +201,7 @@
<header class="topbar">
<a class="topbar-brand" href="https://openvibe.live">
<i class="fa-solid fa-circle-nodes"></i>
-- <span><span class="openvibe">OpenVibe</span>Streamer</span>
++ <span><span class="openvibe">OpenVibe</span>.Live</span>
</a>
<span class="topbar-sep">/</span>
<span class="topbar-page">DMCA &amp; Copyright Policy</span>
ddiff --git a/public/index.html b/public/index.html
iindex 4cf354a5..5aa586a1 100644
---- a/public/index.html
++++ b/public/index.html
@@@ -89,7 +89,7 @@
<i class="fa-solid fa-circle-nodes"></i>
<span class="brand-text-wrap">
<span class="alpha-badge">ALPHA</span>
-- <span class="brand-text"><span class="brand-openvibe">OpenVibe</span><strong class="brand-streamer">Streamer</strong></span>
++ <span class="brand-text"><span class="brand-openvibe">OpenVibe</span><strong class="brand-streamer">.Live</strong></span>
</span>
</a>
<div class="nav-links-wrap">
@@@ -121,7 +121,7 @@
<a href="/dashboard" id="nav-golive-dash" onclick="return dashNav(event)"><i class="fa-solid fa-gauge-high"></i> Dashboard</a>
</div>
</div>
-- <a href="https://openvibe.tools/admin" class="nav-link" data-page="admin" id="nav-admin" style="display:none" data-tooltip="Admin" rel="noopener noreferrer">
++ <a href="https://openvibe.network/admin" class="nav-link" data-page="admin" id="nav-admin" style="display:none" data-tooltip="Admin" rel="noopener noreferrer">
<i class="fa-solid fa-shield-halved"></i> <span>Admin</span>
</a>
</div>
@@@ -149,7 +149,7 @@
<div class="user-dropdown" id="user-dropdown">
<a href="/dashboard" onclick="return handleLinkClick(event, '/dashboard')"><i class="fa-solid fa-gauge-high"></i> My Dashboard</a>
<a href="#" onclick="return handleLinkClick(event, channelPath(currentUser?.username))"><i class="fa-solid fa-user"></i> My Channel</a>
-- <a href="https://openvibe.tools/admin" target="_blank" rel="noopener" id="user-dropdown-admin" style="display:none"><i class="fa-solid fa-shield-halved"></i> Admin Panel</a>
++ <a href="https://openvibe.network/admin" target="_blank" rel="noopener" id="user-dropdown-admin" style="display:none"><i class="fa-solid fa-shield-halved"></i> Admin Panel</a>
<hr>
<a href="https://openvibe.network" target="_blank"><i class="fa-solid fa-network-wired"></i> OpenVibe</a>
<a href="#" onclick="event.preventDefault();showModal('buy-funds')"><i class="fa-solid fa-coins"></i> Support Us</a>
@@@ -1811,7 +1811,7 @@
</div>
<div style="display:flex;gap:10px;align-items:center;margin-top:14px;flex-wrap:wrap">
<button class="btn btn-primary btn-small" onclick="saveSettingsProfile()"><i class="fa-solid fa-floppy-disk"></i> Save Profile</button>
-- <a href="https://openvibe.network" target="_blank" rel="noopener" class="btn btn-outline btn-small" style="text-decoration:none"><i class="fa-solid fa-arrow-up-right-from-square"></i> Manage account on openvibe.tools</a>
++ <a href="https://openvibe.network" target="_blank" rel="noopener" class="btn btn-outline btn-small" style="text-decoration:none"><i class="fa-solid fa-arrow-up-right-from-square"></i> Manage account on openvibe.network</a>
</div>
</div>
@@@ -3493,7 +3493,7 @@ WantedBy=multi-user.target</code>
<tr><td>RTMP</td><td>Node-Media-Server</td><td>OBS classic ingest</td></tr>
<tr><td>Chat/Control</td><td>WebSocket (ws)</td><td>Real-time messaging</td></tr>
<tr><td>Frontend</td><td>Vanilla JS SPA</td><td>No framework</td></tr>
--<tr><td>Auth</td><td>RS256 JWT via openvibe.tools SSO</td><td>Shared across OpenVibe</td></tr>
++<tr><td>Auth</td><td>RS256 JWT via openvibe.network SSO</td><td>Shared across OpenVibe</td></tr>
</tbody>
</table>
@@@ -3519,7 +3519,7 @@ WantedBy=multi-user.target</code>
<thead><tr><th>Service</th><th>URL</th><th>Owns</th></tr></thead>
<tbody>
<tr><td>OpenVibe.Live</td><td>openvibe.live</td><td>Streaming, chat, TTS, controls, VODs, soundboard</td></tr>
--<tr><td>openvibe.tools</td><td>openvibe.tools / openvibe.network</td><td>SSO, accounts, notifications, push, Discord bot, admin</td></tr>
++<tr><td>openvibe.network</td><td>openvibe.network</td><td>SSO, accounts, notifications, push, Discord bot, admin</td></tr>
<tr><td>openvibe.games</td><td>openvibe.games</td><td>Games (RPG, canvas)</td></tr>
</tbody>
</table>
@@@ -3538,7 +3538,7 @@ WantedBy=multi-user.target</code>
<h3>Service Map</h3>
<pre><code>┌──────────────────────────────────────────────────────┐
--│ openvibe.tools (port 3100) — Central Hub │
++│ openvibe.network (port 3100) — Central Hub │
│ ├─ /api/auth — SSO (RS256 JWT) │
│ ├─ /api/notifications — Notification CRUD │
│ ├─ /api/push — Web Push (VAPID) │
@@@ -3562,13 +3562,13 @@ WantedBy=multi-user.target</code>
<h3>Event Flow: Stream Goes Live</h3>
<pre><code>1. Streamer calls POST /api/streams (OpenVibe.Live)
2. OpenVibe.Live creates stream record, starts media endpoint
--3. OpenVibe.Live calls POST /internal/events/stream-live (openvibe.tools)
--4. openvibe.tools Discord bot sends embed to #alerts channel (with dedupe)
--5. openvibe.tools creates STREAM_LIVE notifications for:
++3. OpenVibe.Live calls POST /internal/events/stream-live (openvibe.network)
++4. openvibe.network Discord bot sends embed to #alerts channel (with dedupe)
++5. openvibe.network creates STREAM_LIVE notifications for:
- All followers of the streamer
- Users opted into "all live" alerts
6. Push notifications fire via Web Push (VAPID)
--7. Fallback: OpenVibe.Live sends direct webhook if openvibe.tools unreachable</code></pre>
++7. Fallback: OpenVibe.Live sends direct webhook if openvibe.network unreachable</code></pre>
<h3>WebSocket Services</h3>
<table class="doc-table">
@@@ -3582,10 +3582,10 @@ WantedBy=multi-user.target</code>
</table>
<h3>Admin Proxy Architecture</h3>
--<p>openvibe.tools admin panel proxies requests to OpenVibe.Live:</p>
--<pre><code>openvibe.tools /api/admin/streamer/* → OpenVibe.Live /api/admin/*
--openvibe.tools /api/admin/streamer-tts/* → OpenVibe.Live /api/tts/*
--openvibe.tools /api/admin/streamer-mod/* → OpenVibe.Live /api/mod/*</code></pre>
++<p>openvibe.network admin panel proxies requests to OpenVibe.Live:</p>
++<pre><code>openvibe.network /api/admin/streamer/* → OpenVibe.Live /api/admin/*
++openvibe.network /api/admin/streamer-tts/* → OpenVibe.Live /api/tts/*
++openvibe.network /api/admin/streamer-mod/* → OpenVibe.Live /api/mod/*</code></pre>
<p>Protected by X-Internal-Key header for server-to-server calls.</p>
</div>
@@@ -3595,13 +3595,13 @@ openvibe.tools /api/admin/streamer-mod/* → OpenVibe.Live /api/mod/*</code></
<div class="doc-tab-content" data-doc-tab="auth" style="display:none">
<h2>1. AUTHENTICATION</h2>
--<p><strong>All identity is OpenVibe.Tools SSO.</strong> There is no local password login on OpenVibe.Live &mdash; <code>POST /api/auth/register</code> and <code>POST /api/auth/login</code> are permanently disabled (return <code>410</code>). Humans sign in through openvibe.tools OAuth2; OpenVibe.Live receives an <strong>RS256 JWT</strong> signed by openvibe.tools and verifies it locally. For bots and scripts, use a long-lived <strong><code>hbt_</code> API token</strong> (below) &mdash; no browser login needed.</p>
++<p><strong>All identity is OpenVibe SSO.</strong> There is no local password login on OpenVibe.Live &mdash; <code>POST /api/auth/register</code> and <code>POST /api/auth/login</code> are permanently disabled (return <code>410</code>). Humans sign in through openvibe.network OAuth2; OpenVibe.Live receives an <strong>RS256 JWT</strong> signed by openvibe.network and verifies it locally. For bots and scripts, use a long-lived <strong><code>hbt_</code> API token</strong> (below) &mdash; no browser login needed.</p>
<h3>How auth is sent</h3>
<p>Every authenticated request carries a token as <code>Authorization: Bearer &lt;token&gt;</code> (also read from the <code>ov_token</code> cookie, or a <code>?token=</code> query param on WebSocket URLs). <code>requireAuth</code> accepts either:</p>
<ul>
<li><strong>API token</strong> &mdash; a string starting with <code>hbt_</code>. Best for bots/integrations. Cannot itself create/list/revoke tokens.</li>
--<li><strong>openvibe.tools JWT (RS256)</strong> &mdash; from the SSO login flow; auto-links/creates a local account. Roles come from the token.</li>
++<li><strong>openvibe.network JWT (RS256)</strong> &mdash; from the SSO login flow; auto-links/creates a local account. Roles come from the token.</li>
</ul>
<p><strong>Roles:</strong> <code>admin</code> &gt; <code>global_mod</code> &gt; <code>streamer</code> &gt; <code>user</code> (+ anonymous). Banned accounts get <code>403</code>.</p>
@@@ -3609,7 +3609,7 @@ openvibe.tools /api/admin/streamer-mod/* → OpenVibe.Live /api/mod/*</code></
<table class="doc-table">
<thead><tr><th>Method</th><th>Path</th><th>Auth</th><th>Notes</th></tr></thead>
<tbody>
--<tr><td>GET</td><td>/api/auth/sso/login</td><td>No</td><td>302 &rarr; openvibe.tools OAuth authorize</td></tr>
++<tr><td>GET</td><td>/api/auth/sso/login</td><td>No</td><td>302 &rarr; openvibe.network OAuth authorize</td></tr>
<tr><td>GET</td><td>/api/auth/callback?code&amp;state</td><td>No</td><td>OAuth code&rarr;token exchange; sets cookies</td></tr>
<tr><td>POST</td><td>/api/auth/refresh</td><td>Cookie</td><td>Refresh access token via httpOnly <code>ov_refresh</code> cookie</td></tr>
<tr><td>POST</td><td>/api/auth/logout</td><td>No</td><td>Clear auth cookies</td></tr>
@@@ -4445,8 +4445,8 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
<div class="doc-tab-content" data-doc-tab="integrations" style="display:none">
<h2>INTEGRATIONS — DISCORD, PUSH, WEBHOOKS</h2>
--<h3>Discord Bot (via openvibe.tools)</h3>
--<p>The Discord bot runs on openvibe.tools (not OpenVibe.Live). When a stream goes live, OpenVibe.Live fires an internal event, and the bot posts an embed in the configured alerts channel.</p>
++<h3>Discord Bot (via openvibe.network)</h3>
++<p>The Discord bot runs on openvibe.network (not OpenVibe.Live). When a stream goes live, OpenVibe.Live fires an internal event, and the bot posts an embed in the configured alerts channel.</p>
<table class="doc-table">
<thead><tr><th>Feature</th><th>Description</th></tr></thead>
<tbody>
@@@ -4457,7 +4457,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
</tbody>
</table>
--<h3>Discord Admin API (openvibe.tools)</h3>
++<h3>Discord Admin API (openvibe.network)</h3>
<table class="doc-table">
<thead><tr><th>Method</th><th>Path</th><th>Description</th></tr></thead>
<tbody>
@@@ -4503,7 +4503,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
</table>
<h3>Internal Events API (server-to-server)</h3>
--<p>OpenVibe.Live emits events to openvibe.tools via internal API. Protected by X-Internal-Key header.</p>
++<p>OpenVibe.Live emits events to openvibe.network via internal API. Protected by X-Internal-Key header.</p>
<table class="doc-table">
<thead><tr><th>Method</th><th>Path</th><th>Description</th></tr></thead>
<tbody>
@@@ -4514,7 +4514,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
</table>
<h3>Fallback Webhook (OpenVibe.Live direct)</h3>
--<p>If openvibe.tools is unreachable, OpenVibe.Live falls back to its built-in Discord webhook sender. Configure the webhook URL in OpenVibe.Live site settings (<code>discord_webhook_url</code>).</p>
++<p>If openvibe.network is unreachable, OpenVibe.Live falls back to its built-in Discord webhook sender. Configure the webhook URL in OpenVibe.Live site settings (<code>discord_webhook_url</code>).</p>
<h3>Multistream / Restream — <code>/api/restream</code></h3>
<p>Relay a live stream out to YouTube / Twitch / Kick / custom RTMP. Manage destinations per slot (max 10); stream keys are masked on read. All <code>requireAuth</code>.</p>
@@@ -4682,7 +4682,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
<tr><td>GET / PUT</td><td>/api/news/my-settings</td><td>Yes</td><td>Per-channel news on/off (admin manages sources at <code>/api/news/sources</code>)</td></tr>
<tr><td>GET</td><td>/api/home/hero</td><td>No</td><td>Landing hero: <code>{ stats, media[], slogans }</code></td></tr>
<tr><td>GET</td><td>/api/thumbnails/:filename</td><td>No</td><td>Serve a thumbnail (<code>POST /live/:streamId</code> uploads a live thumb)</td></tr>
--<tr><td>GET</td><td>/api/themes &middot; /themes/me</td><td>No/Yes</td><td>List community themes / my active theme (themes tie into openvibe.tools SSO)</td></tr>
++<tr><td>GET</td><td>/api/themes &middot; /themes/me</td><td>No/Yes</td><td>List community themes / my active theme (themes tie into openvibe.network SSO)</td></tr>
<tr><td>GET</td><td>/api/meta/link-preview?url=</td><td>No</td><td>Link preview / OEmbed data</td></tr>
<tr><td>GET</td><td>/api/health &middot; /updates</td><td>No</td><td>Health check / recent changelog (git commits)</td></tr>
</tbody>
@@@ -4904,18 +4904,18 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
<!-- Scripts -->
<script src="/js/theme-engine.js?v=4"></script>
<script src="/live-notify.js?v=2" defer></script>
--<script src="/js/app.js?v=117" defer></script>
++<script src="/js/app.js?v=118" defer></script>
<script src="/js/stream-player.js?v=17" defer></script>
<script src="/js/chat-shared.js?v=3" defer></script>
<script src="/js/emotes.js?v=8" defer></script>
<script src="/js/gif-picker.js?v=1" defer></script>
--<script src="/js/chat.js?v=69" defer></script>
++<script src="/js/chat.js?v=70" defer></script>
<script src="/js/chat-autocomplete.js?v=1" defer></script>
<script src="/js/channel-uploads.js?v=12" defer></script>
<script src="/js/controls.js?v=8" defer></script>
<script src="/js/onvif-dashboard.js?v=5" defer></script>
--<script src="/js/dashboard.js?v=15" defer></script>
--<script src="/js/admin.js?v=6" defer></script>
++<script src="/js/dashboard.js?v=16" defer></script>
++<script src="/js/admin.js?v=7" defer></script>
<!-- ── Floating Global Chat Widget (visible on non-chat pages) ── -->
<div id="floating-chat-widget" class="floating-chat-widget" style="display:none">
<div class="fcw-header" onmousedown="fcwStartDrag(event)">
@@@ -4959,8 +4959,8 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
</div>
</div>
--<script src="/js/openvibe-bucks.js?v=7" defer></script>
--<script src="/js/openvibe-coins.js?v=5" defer></script>
++<script src="/js/vibes.js?v=8" defer></script>
++<script src="/js/opencoins.js?v=6" defer></script>
<script src="/js/cosmetics-ui.js?v=2" defer></script>
<!-- Broadcast modules — load order matters: state → devices → vods → workspace → core -->
<script src="/js/broadcast-state.js?v=1" defer></script>
@@@ -4970,7 +4970,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
<script src="/js/broadcast.js?v=25" defer></script>
<script src="/js/call.js?v=3" defer></script>
<script src="/js/voice-channels.js?v=2" defer></script>
--<script src="/js/settings.js?v=9" defer></script>
++<script src="/js/settings.js?v=10" defer></script>
<script src="/js/staff-console.js?v=1" defer></script>
<script src="/js/dashboard-moderation.js?v=5" defer></script>
<script src="/js/ai-viewers.js?v=1" defer></script>
@@@ -4987,11 +4987,11 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
// Initialize OpenVibe notifications and theme sync (navbar is handled natively by openvibelive).
// Runs on DOMContentLoaded so the now-deferred shared libs (OpenVibeThemeLoader / OpenVibeNotifications)
// have executed first.
--document.addEventListener('DOMContentLoaded', function initNet.OpenVibework() {
++document.addEventListener('DOMContentLoaded', function initOpenVibeNetwork() {
const token = localStorage.getItem('token') || (document.cookie.match(/(?:^|; )ov_token=([^;]*)/) || [])[1] || null;
if (window.OpenVibeThemeLoader && typeof OpenVibeThemeLoader.init === 'function') {
-- // Themes are owned centrally by openvibe.tools and synced everywhere — pull the
++ // Themes are owned centrally by openvibe.network and synced everywhere — pull the
// user's active theme from there (configured at openvibe.network/themes).
OpenVibeThemeLoader.init({ apiBase: 'https://openvibe.network' });
}
@@@ -4999,7 +4999,7 @@ document.addEventListener('DOMContentLoaded', function initNet.OpenVibework() {
// Notifications bell
const mount = document.getElementById('openvibe-bell-mount');
if (mount && window.OpenVibeNotifications && token) {
-- const openvibeToolsBase = (window.Net.OpenVibeworkUrls && window.Net.OpenVibeworkUrls.tools)
++ const openvibeToolsBase = (window.OpenVibeNetworkUrls && window.OpenVibeNetworkUrls.tools)
|| (['localhost', '127.0.0.1'].includes(window.location.hostname)
? 'http://localhost:3100'
: (['topenvibe.tools', 'topenvibelive.com', 'topenvibe.quest'].includes(window.location.hostname)
ddiff --git a/public/js/admin.js b/public/js/admin.js
iindex f070a689..bbe90991 100644
---- a/public/js/admin.js
++++ b/public/js/admin.js
@@@ -770,7 +770,7 @@ async function loadAdminCashouts() {
<tbody>${cashouts.map(co => `
<tr>
<td>${esc(co.username || '-')}</td>
-- <td>${Number(co.amount).toLocaleString()} HB</td>
++ <td>${Number(co.amount).toLocaleString()} Vibes</td>
<td>$${(co.amount * 0.01).toFixed(2)}</td>
<td>${esc(co.paypal_email || '-')}</td>
<td>${new Date(co.created_at).toLocaleString()}</td>
ddiff --git a/public/js/app.js b/public/js/app.js
iindex 104ecc4c..8f5a789d 100644
---- a/public/js/app.js
++++ b/public/js/app.js
@@@ -12,7 +12,7 @@ let _streamSwitchToken = 0; // increments each stream switch; guards against fas
let openvibeAppMetaData = null;
let openvibeAppMetaPromise = null;
--function getDefaultNet.OpenVibeworkUrls() {
++function getDefaultOpenVibeNetworkUrls() {
const host = window.location.hostname;
const isLocalHost = ['localhost', '127.0.0.1'].includes(host);
const isTopenvibeAlias = ['topenvibe.tools', 'topenvibelive.com', 'topenvibe.quest'].includes(host);
@@@ -22,17 +22,17 @@ function getDefaultNet.OpenVibeworkUrls() {
};
}
--function getNet.OpenVibeworkUrl(service) {
-- const urls = window.Net.OpenVibeworkUrls || getDefaultNet.OpenVibeworkUrls();
-- return (urls && urls[service]) ? urls[service] : getDefaultNet.OpenVibeworkUrls()[service];
++function getOpenVibeNetworkUrl(service) {
++ const urls = window.OpenVibeNetworkUrls || getDefaultOpenVibeNetworkUrls();
++ return (urls && urls[service]) ? urls[service] : getDefaultOpenVibeNetworkUrls()[service];
}
--function getOpenVibe.ToolsUrl() {
-- return getNet.OpenVibeworkUrl('tools');
++function getOpenVibeToolsUrl() {
++ return getOpenVibeNetworkUrl('tools');
}
function getScraplandiaUrl() {
-- return getNet.OpenVibeworkUrl('quest');
++ return getOpenVibeNetworkUrl('quest');
}
/** Cached external viewer count (Kick/Twitch/RS) — updated by cumulative viewer poll */
let _cachedExternalViewerCount = 0;
@@@ -636,7 +636,7 @@ function showModal(id) {
<a href="/api/auth/sso/login" class="btn btn-lg" style="width:100%;display:flex;align-items:center;justify-content:center;gap:8px;background:linear-gradient(135deg,#8b5cf6,#6d28d9);color:#fff;text-decoration:none;border:none;cursor:pointer">
<i class="fa-solid fa-network-wired"></i> Sign in with OpenVibe
</a>
-- <p style="text-align:center;margin-top:12px;font-size:12px;color:var(--text-muted)">Registration is handled on openvibe.tools</p>`,
++ <p style="text-align:center;margin-top:12px;font-size:12px;color:var(--text-muted)">Registration is handled on openvibe.network</p>`,
donate: openvibeBucksDonateModal(),
'buy-funds': openvibeBucksBuyModal(),
cashout: openvibeBucksCashoutModal(),
@@@ -939,10 +939,10 @@ function routeFromURL() {
showPage('broadcast');
loadBroadcastPage();
} else if (segments[0] === 'admin') {
-- window.location.href = `${getOpenVibe.ToolsUrl()}/admin`;
++ window.location.href = `${getOpenVibeToolsUrl()}/admin`;
return;
} else if (segments[0] === 'themes') {
-- // Theme management moved to the central openvibe.tools account app.
++ // Theme management moved to the central openvibe.network account app.
window.location.href = 'https://openvibe.network/themes';
return;
} else if (segments[0] === 'chat') {
@@@ -4892,7 +4892,7 @@ function _goalWidgetItemHTML(g) {
<div class="cgw-body">
<div class="cgw-top">
<span class="cgw-title">${reached ? '🎉 ' : ''}${esc(g.title)}</span>
-- <span class="cgw-amt">${Number(g.current_amount).toLocaleString()} / ${Number(g.target_amount).toLocaleString()} HB</span>
++ <span class="cgw-amt">${Number(g.current_amount).toLocaleString()} / ${Number(g.target_amount).toLocaleString()} Vibes</span>
<span class="cgw-pct">${reached ? '✓' : pct + '%'}</span>
</div>
</div>
@@@ -8013,8 +8013,8 @@ document.addEventListener('DOMContentLoaded', async () => {
// Start proactive token refresh timer
startTokenRefreshTimer();
-- // Theme is owned centrally by openvibe.tools and applied by the shared OpenVibeThemeLoader
-- // (which syncs from openvibe.tools after paint). We no longer read OpenVibe.Live's local
++ // Theme is owned centrally by openvibe.network and applied by the shared OpenVibeThemeLoader
++ // (which syncs from openvibe.network after paint). We no longer read OpenVibe.Live's local
// theme store, so the theme chosen at openvibe.network/themes is authoritative here.
// Route from current URL instead of always going home — but not in the popout
ddiff --git a/public/js/camp-funds.js b/public/js/camp-funds.js
iindex dde01515..7811f9e9 100644
---- a/public/js/camp-funds.js
++++ b/public/js/camp-funds.js
@@@ -1,6 +1,6 @@
/* ═══════════════════════════════════════════════════════════════
OpenVibe.Live — Vibes (Virtual Currency UI)
-- 1 OpenVibe Buck = $1.00 USD
++ 1 Vibe = $1.00 USD
═══════════════════════════════════════════════════════════════ */
/**
@@@ -9,17 +9,17 @@
function openvibeBucksBuyModal() {
return `
<h3><i class="fa-solid fa-coins"></i> Buy Vibes</h3>
-- <p class="muted" style="margin-bottom:16px">1 OpenVibe Buck = $1.00</p>
++ <p class="muted" style="margin-bottom:16px">1 Vibe = $1.00</p>
<div class="form-group">
<label>Amount</label>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:12px">
-- <button class="btn btn-outline" onclick="setBuyAmount(5)">5 HB<br><small>$5</small></button>
-- <button class="btn btn-outline" onclick="setBuyAmount(10)">10 HB<br><small>$10</small></button>
-- <button class="btn btn-outline" onclick="setBuyAmount(25)">25 HB<br><small>$25</small></button>
-- <button class="btn btn-outline" onclick="setBuyAmount(50)">50 HB<br><small>$50</small></button>
-- <button class="btn btn-outline" onclick="setBuyAmount(100)">100 HB<br><small>$100</small></button>
-- <button class="btn btn-outline" onclick="setBuyAmount(250)">250 HB<br><small>$250</small></button>
++ <button class="btn btn-outline" onclick="setBuyAmount(5)">5 Vibes<br><small>$5</small></button>
++ <button class="btn btn-outline" onclick="setBuyAmount(10)">10 Vibes<br><small>$10</small></button>
++ <button class="btn btn-outline" onclick="setBuyAmount(25)">25 Vibes<br><small>$25</small></button>
++ <button class="btn btn-outline" onclick="setBuyAmount(50)">50 Vibes<br><small>$50</small></button>
++ <button class="btn btn-outline" onclick="setBuyAmount(100)">100 Vibes<br><small>$100</small></button>
++ <button class="btn btn-outline" onclick="setBuyAmount(250)">250 Vibes<br><small>$250</small></button>
</div>
</div>
@@@ -81,12 +81,12 @@ function openvibeBucksDonateModal() {
<p class="muted" style="margin-bottom:16px">Support this streamer!</p>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:12px">
-- <button class="btn btn-outline" onclick="setDonateAmount(1)">1 HB</button>
-- <button class="btn btn-outline" onclick="setDonateAmount(2)">2 HB</button>
-- <button class="btn btn-outline" onclick="setDonateAmount(5)">5 HB</button>
-- <button class="btn btn-outline" onclick="setDonateAmount(10)">10 HB</button>
-- <button class="btn btn-outline" onclick="setDonateAmount(25)">25 HB</button>
-- <button class="btn btn-outline" onclick="setDonateAmount(50)">50 HB</button>
++ <button class="btn btn-outline" onclick="setDonateAmount(1)">1 Vibes</button>
++ <button class="btn btn-outline" onclick="setDonateAmount(2)">2 Vibes</button>
++ <button class="btn btn-outline" onclick="setDonateAmount(5)">5 Vibes</button>
++ <button class="btn btn-outline" onclick="setDonateAmount(10)">10 Vibes</button>
++ <button class="btn btn-outline" onclick="setDonateAmount(25)">25 Vibes</button>
++ <button class="btn btn-outline" onclick="setDonateAmount(50)">50 Vibes</button>
</div>
<div class="form-group">
ddiff --git a/public/js/chat.js b/public/js/chat.js
iindex 4fbdb5f8..def7de48 100644
---- a/public/js/chat.js
++++ b/public/js/chat.js
@@@ -3288,7 +3288,7 @@ function addDonationGoalMessage(msg) {
const g = msg.goal || {};
const by = esc(msg.by || 'the community');
const title = esc(g.title || 'Goal');
-- const target = (g.target_amount != null && g.target_amount !== '') ? `${Number(g.target_amount).toLocaleString()} HB` : '';
++ const target = (g.target_amount != null && g.target_amount !== '') ? `${Number(g.target_amount).toLocaleString()} Vibes` : '';
const media = g.image_url
? (g.media_type === 'video'
? `<video class="cgr-media" src="${esc(g.image_url)}" muted loop autoplay playsinline></video>`
ddiff --git a/public/js/dashboard.js b/public/js/dashboard.js
iindex 4b9fce58..d545467f 100644
---- a/public/js/dashboard.js
++++ b/public/js/dashboard.js
@@@ -978,7 +978,7 @@ async function loadDashFunds() {
const set = (id, v) => { const el = document.getElementById(id); if (el) el.textContent = v; };
// Integer Vibes; USD shown is the cashout value (100 bucks = $1).
set('dash-funds-amount', bal.toLocaleString());
-- set('dash-funds-usd', `(${bal.toLocaleString()} HB)`);
++ set('dash-funds-usd', `(${bal.toLocaleString()} Vibes)`);
set('dash-cashout-amount', cash.toLocaleString());
set('dash-cashout-usd', `($${(cash / 100).toFixed(2)})`);
} catch { /* silent */ }
@@@ -1001,7 +1001,7 @@ function _bucksTxRow(t) {
return `<div class="bucks-log-row">
<div class="bucks-log-icon"><i class="fa-solid ${icon}"></i></div>
<div class="bucks-log-main"><div class="bucks-log-label">${label}</div><div class="bucks-log-when">${esc(when)}</div>${note}</div>
-- <div class="bucks-log-amt ${cls}">${sign}${amt.toLocaleString()} HB</div>
++ <div class="bucks-log-amt ${cls}">${sign}${amt.toLocaleString()} Vibes</div>
</div>`;
}
ddiff --git a/public/js/game.js b/public/js/game.js
iindex e5ce79aa..a76ba94d 100644
---- a/public/js/game.js
++++ b/public/js/game.js
@@@ -3129,7 +3129,6 @@ function interactWithNPC(npc) {
}
panelAnimProgress = 0;
panelAnimDir = 1;
-- }
loadNPCData(npc.id);
}
ddiff --git a/public/js/notification-ui.js b/public/js/notification-ui.js
iindex 71e8bb16..cea00585 100644
---- a/public/js/notification-ui.js
++++ b/public/js/notification-ui.js
@@@ -14,7 +14,7 @@
const MAX_TOASTS = 3;
const SOUNDS = { normal: 'notification.mp3', high: 'notification-high.mp3', critical: 'notification-alarm.mp3' };
-- function getDefaultOpenVibe.ToolsUrl() {
++ function getDefaultOpenVibeToolsUrl() {
const host = window.location.hostname;
const isLocalHost = ['localhost', '127.0.0.1'].includes(host);
const isTopenvibeAlias = ['topenvibe.tools', 'topenvibelive.com', 'topenvibe.quest'].includes(host);
@@@ -23,7 +23,7 @@
let _config = {
token: null,
-- apiBase: (window.Net.OpenVibeworkUrls && window.Net.OpenVibeworkUrls.tools) || getDefaultOpenVibe.ToolsUrl(),
++ apiBase: (window.OpenVibeNetworkUrls && window.OpenVibeNetworkUrls.tools) || getDefaultOpenVibeToolsUrl(),
soundBase: '/assets/sounds',
onAction: null,
};
@@@ -568,7 +568,7 @@
if (!('serviceWorker' in navigator) || !('PushManager' in window)) return { error: 'Push not supported' };
try {
const reg = await navigator.serviceWorker.register('/service-worker.js');
-- // Fetch VAPID public key from openvibe.tools
++ // Fetch VAPID public key from openvibe.network
const keyRes = await apiFetch('/api/push/vapid-key');
if (!keyRes?.publicKey) return { error: 'Push not configured on server' };
ddiff --git a/public/js/opencoins.js b/public/js/opencoins.js
iindex 3f31b5fd..8e9e8bc3 100644
---- a/public/js/opencoins.js
++++ b/public/js/opencoins.js
@@@ -232,7 +232,7 @@ function handleCoinEarned(msg) {
function renderRedemption(msg, container) {
const el = document.createElement('div');
el.className = 'chat-msg redemption';
-- el.innerHTML = `<i class="fa-solid fa-gem" style="color:${esc(msg.reward_color || 'var(--accent)')}"></i> <strong>${esc(msg.username || 'Someone')}</strong> redeemed <strong>${esc(msg.reward_title || 'a reward')}</strong>${msg.user_input ? `: ${esc(msg.user_input)}` : ''} <span class="muted">(${(msg.cost || 0).toLocaleString()} nickels)</span>`;
++ el.innerHTML = `<i class="fa-solid fa-gem" style="color:${esc(msg.reward_color || 'var(--accent)')}"></i> <strong>${esc(msg.username || 'Someone')}</strong> redeemed <strong>${esc(msg.reward_title || 'a reward')}</strong>${msg.user_input ? `: ${esc(msg.user_input)}` : ''} <span class="muted">(${(msg.cost || 0).toLocaleString()} ${esc(channelPointsName())})</span>`;
container.appendChild(el);
container.scrollTop = container.scrollHeight;
}
ddiff --git a/public/js/settings.js b/public/js/settings.js
iindex e6f698ed..25ac7818 100644
---- a/public/js/settings.js
++++ b/public/js/settings.js
@@@ -198,7 +198,7 @@ async function saveSettingsProfile() {
email: document.getElementById('set-email').value.trim() || null,
bio: document.getElementById('set-bio').value.trim(),
};
-- // Profile color is managed centrally at openvibe.tools now (its SSO sync overrides any
++ // Profile color is managed centrally at openvibe.network now (its SSO sync overrides any
// local value on every login) — only send it if the field is still present.
const _pc = document.getElementById('set-profile-color');
if (_pc) data.profile_color = _pc.value;
@@@ -215,20 +215,20 @@ async function saveSettingsProfile() {
}
}
--function getDefaultOpenVibe.ToolsUrl() {
++function getDefaultOpenVibeToolsUrl() {
const host = window.location.hostname;
const isLocalHost = ['localhost', '127.0.0.1'].includes(host);
const isTopenvibeAlias = ['topenvibe.tools', 'topenvibelive.com', 'topenvibe.quest'].includes(host);
return isLocalHost ? 'http://localhost:3100' : (isTopenvibeAlias ? 'https://topenvibe.tools' : 'https://openvibe.network');
}
--function getOpenVibe.ToolsUrl() {
-- const urls = window.Net.OpenVibeworkUrls || { tools: getDefaultOpenVibe.ToolsUrl() };
-- return urls.tools || getDefaultOpenVibe.ToolsUrl();
++function getOpenVibeToolsUrl() {
++ const urls = window.OpenVibeNetworkUrls || { tools: getDefaultOpenVibeToolsUrl() };
++ return urls.tools || getDefaultOpenVibeToolsUrl();
}
--function getOpenVibe.ToolsAdminUrl() {
-- const url = getOpenVibe.ToolsUrl();
++function getOpenVibeToolsAdminUrl() {
++ const url = getOpenVibeToolsUrl();
try {
const u = new URL(url);
u.hostname = u.hostname.replace(/^www\./, 'my.');
@@@ -238,9 +238,9 @@ function getOpenVibe.ToolsAdminUrl() {
}
}
--// Password management handled on openvibe.tools
++// Password management handled on openvibe.network
function changePassword() {
-- window.open(getOpenVibe.ToolsAdminUrl(), '_blank');
++ window.open(getOpenVibeToolsAdminUrl(), '_blank');
}
/* ── Broadcaster Tab ──────────────────────────────────────────── */
ddiff --git a/public/js/vibes.js b/public/js/vibes.js
iindex 113704c4..a858f55d 100644
---- a/public/js/vibes.js
++++ b/public/js/vibes.js
@@@ -4,7 +4,7 @@
═══════════════════════════════════════════════════════════════ */
// Client mirror of the server volume-discount tiers (server is the source of truth;
--// this is just for the live custom-amount price preview). Keep in sync with openvibe-bucks.js.
++// this is just for the live custom-amount price preview). Keep in sync with vibes.js.
const _BUCKS_TIERS = [[25000, 0.0110], [10000, 0.0115], [5000, 0.0120], [2500, 0.0124], [1000, 0.0130], [500, 0.0140], [0, 0.0150]];
function _priceForBucks(b) {
b = Math.max(0, Math.round(Number(b) || 0));
@@@ -56,7 +56,7 @@ async function _initBuyBucks() {
const packages = (cfg.packages && cfg.packages.length) ? cfg.packages : [100, 500, 1000, 2500, 5000, 10000].map(bucks => ({ bucks, usd: _priceForBucks(bucks) }));
const pkgBox = document.getElementById('buy-packages');
if (pkgBox) pkgBox.innerHTML = packages.map(p =>
-- `<button class="btn btn-outline" onclick="setBuyAmount(${p.bucks})">${p.bucks.toLocaleString()} HB<br><small>$${Number(p.usd).toFixed(2)}</small></button>`
++ `<button class="btn btn-outline" onclick="setBuyAmount(${p.bucks})">${p.bucks.toLocaleString()} Vibes<br><small>$${Number(p.usd).toFixed(2)}</small></button>`
).join('');
const min = cfg.minPurchaseBucks || 100;
@@@ -160,12 +160,12 @@ function openvibeBucksDonateModal() {
<p class="muted" style="margin-bottom:16px">Support this streamer!</p>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:12px">
-- <button class="btn btn-outline" onclick="setDonateAmount(100)">100 HB</button>
-- <button class="btn btn-outline" onclick="setDonateAmount(250)">250 HB</button>
-- <button class="btn btn-outline" onclick="setDonateAmount(500)">500 HB</button>
-- <button class="btn btn-outline" onclick="setDonateAmount(1000)">1,000 HB</button>
-- <button class="btn btn-outline" onclick="setDonateAmount(2500)">2,500 HB</button>
-- <button class="btn btn-outline" onclick="setDonateAmount(5000)">5,000 HB</button>
++ <button class="btn btn-outline" onclick="setDonateAmount(100)">100 Vibes</button>
++ <button class="btn btn-outline" onclick="setDonateAmount(250)">250 Vibes</button>
++ <button class="btn btn-outline" onclick="setDonateAmount(500)">500 Vibes</button>
++ <button class="btn btn-outline" onclick="setDonateAmount(1000)">1,000 Vibes</button>
++ <button class="btn btn-outline" onclick="setDonateAmount(2500)">2,500 Vibes</button>
++ <button class="btn btn-outline" onclick="setDonateAmount(5000)">5,000 Vibes</button>
</div>
<div class="form-group">
@@@ -206,7 +206,7 @@ async function _loadDonateGoals() {
const goals = (data.goals || []).filter(g => g.is_active);
if (!goals.length) { wrap.style.display = 'none'; return; }
sel.innerHTML = `<option value="">General support (no specific goal)</option>` +
-- goals.map(g => `<option value="${g.id}">${escHb(g.title)} — ${Number(g.current_amount).toLocaleString()}/${Number(g.target_amount).toLocaleString()} HB</option>`).join('');
++ goals.map(g => `<option value="${g.id}">${escHb(g.title)} — ${Number(g.current_amount).toLocaleString()}/${Number(g.target_amount).toLocaleString()} Vibes</option>`).join('');
wrap.style.display = '';
// Pre-select a goal if the donate modal was opened from a goal popover.
if (window._pendingDonateGoalId) {
@@@ -251,7 +251,7 @@ async function doDonate() {
function openvibeBucksCashoutModal() {
return `
<h3><i class="fa-solid fa-money-bill-transfer"></i> Cash Out</h3>
-- <p class="muted" style="margin-bottom:16px">Draws from your <strong>cashout balance</strong> (Vibes sent to you). 100 Bucks = $1.00. Minimum 500 HB ($5.00). Held in escrow until admin approves.</p>
++ <p class="muted" style="margin-bottom:16px">Draws from your <strong>cashout balance</strong> (Vibes sent to you). 100 Vibes = $1.00. Minimum 500 Vibes ($5.00). Held in escrow until admin approves.</p>
<div class="form-group">
<label>Amount (Vibes)</label>
ddiff --git a/public/popout-chat.html b/public/popout-chat.html
iindex b267a980..63a82a18 100644
---- a/public/popout-chat.html
++++ b/public/popout-chat.html
@@@ -262,14 +262,14 @@
<!-- Chat feature scripts — mirror index.html so the popout has full parity.
Order: dependencies (app/emotes/gif/uploads/coins/bucks/cosmetics) before chat.js. -->
-- <script src="/js/app.js?v=51"></script>
++ <script src="/js/app.js?v=52"></script>
<script src="/js/emotes.js?v=8"></script>
<script src="/js/gif-picker.js?v=1"></script>
<script src="/js/channel-uploads.js?v=6"></script>
-- <script src="/js/openvibe-bucks.js?v=3"></script>
-- <script src="/js/openvibe-coins.js?v=4"></script>
++ <script src="/js/vibes.js?v=8"></script>
++ <script src="/js/opencoins.js?v=6"></script>
<script src="/js/cosmetics-ui.js?v=2"></script>
-- <script src="/js/chat.js?v=40"></script>
++ <script src="/js/chat.js?v=41"></script>
<script src="/js/chat-autocomplete.js?v=1"></script>
<script>
'use strict';
ddiff --git a/public/privacy.html b/public/privacy.html
iindex 5aeee1a1..e7a970fb 100644
---- a/public/privacy.html
++++ b/public/privacy.html
@@@ -197,7 +197,7 @@
<header class="topbar">
<a class="topbar-brand" href="https://openvibe.live">
<i class="fa-solid fa-circle-nodes"></i>
-- <span><span class="openvibe">OpenVibe</span>Streamer</span>
++ <span><span class="openvibe">OpenVibe</span>.Live</span>
</a>
<span class="topbar-sep">/</span>
<span class="topbar-page">Privacy Policy</span>
@@@ -217,7 +217,7 @@
<h2><i class="fa-solid fa-box-archive"></i> What We Collect</h2>
<p>Only what's needed to run a streaming platform:</p>
<ul class="rule-list">
-- <li class="yes"><span class="rule-icon"><i class="fa-solid fa-user"></i></span><span><strong>Account info.</strong> Your username, email, and profile details — handled through the OpenVibe single sign-on (openvibe.tools). We use it to identify you and keep you logged in.</span></li>
++ <li class="yes"><span class="rule-icon"><i class="fa-solid fa-user"></i></span><span><strong>Account info.</strong> Your username, email, and profile details — handled through the OpenVibe single sign-on (openvibe.network). We use it to identify you and keep you logged in.</span></li>
<li class="yes"><span class="rule-icon"><i class="fa-solid fa-video"></i></span><span><strong>Content you create.</strong> Streams, VODs, clips, pastes, chat messages, themes, and anything else you post. You choose whether each is public, unlisted, or private.</span></li>
<li class="yes"><span class="rule-icon"><i class="fa-solid fa-server"></i></span><span><strong>Technical basics.</strong> IP address, browser/user-agent, and timestamps — used for security, abuse prevention, rate limiting, and rough view counts. We don't build advertising profiles.</span></li>
<li class="yes"><span class="rule-icon"><i class="fa-solid fa-coins"></i></span><span><strong>Payments.</strong> If you buy Vibes or tip, payment processors handle the card details — we never see or store your full card number.</span></li>
ddiff --git a/public/tos.html b/public/tos.html
iindex 493100c0..488b0ee6 100644
---- a/public/tos.html
++++ b/public/tos.html
@@@ -197,7 +197,7 @@
<header class="topbar">
<a class="topbar-brand" href="https://openvibe.live">
<i class="fa-solid fa-circle-nodes"></i>
-- <span><span class="openvibe">OpenVibe</span>Streamer</span>
++ <span><span class="openvibe">OpenVibe</span>.Live</span>
</a>
<span class="topbar-sep">/</span>
<span class="topbar-page">Terms of Service</span>
ddiff --git a/server/admin/routes.js b/server/admin/routes.js
iindex a563f70d..7564d282 100644
---- a/server/admin/routes.js
++++ b/server/admin/routes.js
@@@ -449,7 +449,7 @@ router.get('/settings', (req, res) => {
}
});
--// AI usage + estimated cost breakdown (for the openvibe.tools admin AI tab).
++// AI usage + estimated cost breakdown (for the openvibe.network admin AI tab).
router.get('/ai/usage', (req, res) => {
try {
const days = Math.min(365, Math.max(1, parseInt(req.query.days, 10) || 30));
@@@ -961,22 +961,16 @@ router.delete('/storage/clips/bulk', async (req, res) => {
}
});
--// ── Storage tiering + media tools → moved to OpenVibe.Media ──
--const MOVED_NOTE = {
-- moved: true,
-- service: 'OpenVibe.Media',
-- note: 'Storage tiering (local/B2/R2) and media tools are managed by the OpenVibe.Media service now.',
--};
--for (const route of ['/storage/tiers', '/storage/buckets']) {
-- router.get(route, (req, res) => res.status(200).json({ ...MOVED_NOTE }));
--}
--for (const [method, route] of [
-- ['put', '/storage/tiers/settings'],
-- ['post', '/storage/tiers/sweep'],
-- ['post', '/storage/tiers/move'],
-- ['post', '/storage/tiers/bulk-move'],
++// ── Storage tiering → proxied to OpenVibe.Media (app-key auth) ──
++for (const [method, route, upstream] of [
++ ['get', '/storage/tiers', '/admin/storage/tiers'],
++ ['get', '/storage/buckets', '/admin/storage/buckets'],
++ ['put', '/storage/tiers/settings', '/admin/storage/tiers/settings'],
++ ['post', '/storage/tiers/sweep', '/admin/storage/tiers/sweep'],
++ ['post', '/storage/tiers/move', '/admin/storage/tiers/move'],
++ ['post', '/storage/tiers/bulk-move', '/admin/storage/tiers/bulk-move'],
]) {
-- router[method](route, (req, res) => res.status(501).json({ ...MOVED_NOTE, error: 'Moved to OpenVibe.Media' }));
++ router[method](route, (req, res) => mediaClient.proxy(req, res, upstream, { method: method.toUpperCase() }));
}
// ═══════════════════════════════════════════════════════════════
ddiff --git a/server/ai/ai-analysis.js b/server/ai/ai-analysis.js
iindex 62f5bf5a..80c1a16e 100644
---- a/server/ai/ai-analysis.js
++++ b/server/ai/ai-analysis.js
@@@ -4,7 +4,7 @@
* Vision + text analysis used for: paste description/tags, paste text overviews,
* and periodic live-stream "memories". Provider (Anthropic Claude by default, or
* any OpenAI-compatible endpoint) + key + model are configured in
-- * openvibe.tools/admin → AI. Everything is gated by the `ai_enabled` master switch and
++ * openvibe.network/admin → AI. Everything is gated by the `ai_enabled` master switch and
* an optional daily USD budget cap. Token usage + estimated cost are recorded to
* `ai_usage` for the admin cost breakdown.
*/
ddiff --git a/server/ai/streamer-overview-job.js b/server/ai/streamer-overview-job.js
iindex e6070d8d..c5f30bc1 100644
---- a/server/ai/streamer-overview-job.js
++++ b/server/ai/streamer-overview-job.js
@@@ -7,7 +7,7 @@
* - A "decent" overview (>= DECENT_LEN chars) is refreshed at most once per 12h.
* - A sparse/missing overview is retried hourly until it fills out.
* Only streamers with some signal (memories or VODs) are considered, and it no-ops
-- * entirely until AI is enabled in openvibe.tools/admin → AI.
++ * entirely until AI is enabled in openvibe.network/admin → AI.
*/
'use strict';
const db = require('../db/database');
ddiff --git a/server/auth/auth.js b/server/auth/auth.js
iindex 58211cf8..b7e35feb 100644
---- a/server/auth/auth.js
++++ b/server/auth/auth.js
@@@ -1,7 +1,7 @@
/**
* OpenVibe.Live — JWT Auth Middleware
* All authentication is handled via OpenVibe.Tools RS256 tokens.
-- * Users sign in on openvibe.tools and are redirected back via OAuth2.
++ * Users sign in on openvibe.network and are redirected back via OAuth2.
* Local user records are resolved via linked_accounts.
*/
const jwt = require('jsonwebtoken');
@@@ -13,7 +13,7 @@ const db = require('../db/database');
// ── OpenVibe.Tools Public Key (RS256 verification) ──────────────
let openvibeToolsPublicKey = null;
--// The issuer is the public-facing URL of openvibe.tools (the SSO provider).
++// The issuer is the public-facing URL of openvibe.network (the SSO provider).
// It's initialized from env or registry after config loads; we read it lazily
// at verify-time so config.refreshRegistry() updates are picked up automatically.
const config = require('../config');
@@@ -32,17 +32,17 @@ function loadNetworkPublicKey() {
try {
if (fs.existsSync(p)) {
openvibeToolsPublicKey = fs.readFileSync(p, 'utf8');
-- console.log(`[Auth] Loaded openvibe.tools public key from ${p}`);
++ console.log(`[Auth] Loaded openvibe.network public key from ${p}`);
return;
}
} catch { /* try next */ }
}
-- console.error('[Auth] ❌ openvibe.tools public key not found — authentication will NOT work!');
++ console.error('[Auth] ❌ openvibe.network public key not found — authentication will NOT work!');
}
loadNetworkPublicKey();
/**
-- * Verify a openvibe.tools RS256 JWT token.
++ * Verify a openvibe.network RS256 JWT token.
* Returns decoded payload or null.
*/
function verifyToken(token) {
@@@ -75,13 +75,13 @@ function verifyTokenWithReason(token) {
}
/**
-- * Resolve a openvibe.tools user to a local OpenVibe.Live user.
++ * Resolve a openvibe.network user to a local OpenVibe.Live user.
* Checks linked_accounts first, falls back to username match,
* auto-creates a local account if none found.
*/
--// Keep the local user's role + profile fields in sync with the openvibe.tools SSO token
++// Keep the local user's role + profile fields in sync with the openvibe.network SSO token
// on EVERY auth — not just at account creation — so role changes (e.g. an admin grant
--// in openvibe.tools/admin) propagate. Only writes when a value actually changed. The local
++// in openvibe.network/admin) propagate. Only writes when a value actually changed. The local
// `is_owner` flag is NOT in the token and is never touched here.
const _ROLE_RANK = { user: 0, streamer: 1, global_mod: 2, admin: 3 };
@@@ -95,7 +95,7 @@ function _syncSsoUserFields(user, decoded) {
// live 24h, so a just-promoted admin's older token still carries role:'user'
// and would otherwise strip their role (and Staff badge) on every connect.
// Upgrades apply immediately; downgrades are pushed authoritatively from
-- // openvibe.tools via POST /internal/user-role instead of trusting a stale token.
++ // openvibe.network via POST /internal/user-role instead of trusting a stale token.
if (decoded.role && _ROLE_RANK[decoded.role] !== undefined && decoded.role !== user.role
&& _ROLE_RANK[decoded.role] > (_ROLE_RANK[user.role] ?? 0)) {
updates.push('role = ?'); params.push(decoded.role);
@@@ -123,17 +123,17 @@ function resolveNetworkUser(decoded) {
// Try matching by username (case-insensitive)
let user = db.getUserByUsername(decoded.username);
if (user) {
-- // Auto-link this user to the openvibe.tools account
++ // Auto-link this user to the openvibe.network account
try {
db.getDb().prepare(
"INSERT OR IGNORE INTO linked_accounts (service, service_user_id, service_username, user_id) VALUES ('network', ?, ?, ?)"
).run(openvibeToolsId, decoded.username, user.id);
-- console.log(`[Auth] Auto-linked ${decoded.username} to openvibe.tools id ${openvibeToolsId}`);
++ console.log(`[Auth] Auto-linked ${decoded.username} to openvibe.network id ${openvibeToolsId}`);
} catch { /* already linked */ }
return _syncSsoUserFields(user, decoded);
}
-- // Auto-create a local user for this openvibe.tools account
++ // Auto-create a local user for this openvibe.network account
try {
const stream_key = uuidv4().replace(/-/g, '');
const result = db.createUser({
@@@ -164,7 +164,7 @@ function resolveNetworkUser(decoded) {
"INSERT OR IGNORE INTO linked_accounts (user_id, service, service_user_id, service_username) VALUES (?, 'network', ?, ?)"
).run(user.id, openvibeToolsId, decoded.username);
-- console.log(`[Auth] Auto-created local account for openvibe.tools user ${decoded.username} (local id: ${user.id})`);
++ console.log(`[Auth] Auto-created local account for openvibe.network user ${decoded.username} (local id: ${user.id})`);
return user;
} catch (err) {
console.error(`[Auth] Failed to auto-create user for ${decoded.username}:`, err.message);
@@@ -184,7 +184,7 @@ function authenticateApiToken(rawToken) {
}
/**
-- * Express middleware — requires valid openvibe.tools JWT or API token
++ * Express middleware — requires valid openvibe.network JWT or API token
* Resolves to local user via linked_accounts (auto-creates if needed).
*/
function requireAuth(req, res, next) {
@@@ -205,7 +205,7 @@ function requireAuth(req, res, next) {
return next();
}
-- // Fall back to openvibe.tools JWT
++ // Fall back to openvibe.network JWT
const decoded = verifyToken(token);
if (!decoded) {
return res.status(401).json({ error: 'Invalid or expired token' });
@@@ -344,7 +344,7 @@ function extractWsToken(req) {
/**
* Authenticate a WebSocket connection (returns user or null)
-- * Supports both openvibe.tools JWT and API tokens (hbt_xxx)
++ * Supports both openvibe.network JWT and API tokens (hbt_xxx)
*/
function authenticateWs(token) {
if (!token) return null;
@@@ -368,7 +368,7 @@ function authenticateWs(token) {
}
/**
-- * Reload the openvibe.tools public key (e.g., after key rotation)
++ * Reload the openvibe.network public key (e.g., after key rotation)
*/
function reloadNetworkKey() {
loadNetworkPublicKey();
ddiff --git a/server/auth/routes.js b/server/auth/routes.js
iindex 7940f819..24f42334 100644
---- a/server/auth/routes.js
++++ b/server/auth/routes.js
@@@ -1,9 +1,9 @@
/**
* OpenVibe.Live — Auth Routes
* All authentication is via OpenVibe.Tools OAuth2 SSO.
-- * No local login/register — users sign in at openvibe.tools.
++ * No local login/register — users sign in at openvibe.network.
*
-- * GET /api/auth/sso/login — Redirect to openvibe.tools OAuth
++ * GET /api/auth/sso/login — Redirect to openvibe.network OAuth
* GET /api/auth/callback — OAuth callback
* GET /api/auth/me — Current user
* PUT /api/auth/profile — Update profile
@@@ -87,12 +87,12 @@ const avatarUpload = multer({
},
});
--// ── Register (disabled — use openvibe.tools) ─────────────────────
++// ── Register (disabled — use openvibe.network) ─────────────────────
router.post('/register', (_req, res) => {
res.status(410).json({ error: 'Local registration is no longer available. Please sign in with OpenVibe.' });
});
--// ── Login (disabled — use openvibe.tools) ────────────────────────
++// ── Login (disabled — use openvibe.network) ────────────────────────
router.post('/login', (_req, res) => {
res.status(410).json({ error: 'Local login is no longer available. Please sign in with OpenVibe.' });
});
@@@ -161,9 +161,9 @@ router.put('/profile', requireAuth, (req, res) => {
}
});
--// ── Change Password (disabled — managed on openvibe.tools) ──────
++// ── Change Password (disabled — managed on openvibe.network) ──────
router.post('/change-password', (_req, res) => {
-- res.status(410).json({ error: 'Password management has moved to openvibe.tools.' });
++ res.status(410).json({ error: 'Password management has moved to openvibe.network.' });
});
// ── Get Stream Key ───────────────────────────────────────────
@@@ -287,7 +287,7 @@ function getNetworkRedirectUri() {
return `${config.baseUrl.toLowerCase()}/api/auth/callback`;
}
--// ── Initiate OAuth Login (redirect to openvibe.tools) ───────────
++// ── Initiate OAuth Login (redirect to openvibe.network) ───────────
router.get('/sso/login', (req, res) => {
const state = require('crypto').randomBytes(16).toString('hex');
// Store state in a short-lived cookie for CSRF protection
@@@ -363,7 +363,7 @@ router.get('/callback', async (req, res) => {
return res.status(400).send('No user data in token response');
}
-- // Find or create local user linked to this openvibe.tools account
++ // Find or create local user linked to this openvibe.network account
const openvibeToolsId = String(ssoUser.id);
// Check linked_accounts first
@@@ -399,7 +399,7 @@ router.get('/callback', async (req, res) => {
});
localUser = db.getUserById(result.lastInsertRowid);
-- // Sync optional fields from openvibe.tools
++ // Sync optional fields from openvibe.network
if (ssoUser.avatar_url) db.updateUserAvatar(localUser.id, ssoUser.avatar_url);
if (ssoUser.bio) db.getDb().prepare('UPDATE users SET bio = ? WHERE id = ?').run(ssoUser.bio, localUser.id);
if (ssoUser.role && ['user', 'streamer', 'global_mod', 'admin'].includes(ssoUser.role)) {
@@@ -407,19 +407,19 @@ router.get('/callback', async (req, res) => {
}
if (ssoUser.profile_color) db.getDb().prepare('UPDATE users SET profile_color = ? WHERE id = ?').run(ssoUser.profile_color, localUser.id);
-- // Link to openvibe.tools
++ // Link to openvibe.network
db.getDb().prepare(
"INSERT OR IGNORE INTO linked_accounts (user_id, service, service_user_id, service_username) VALUES (?, 'network', ?, ?)"
).run(localUser.id, openvibeToolsId, ssoUser.username);
localUser = db.getUserById(localUser.id); // re-fetch
-- console.log(`[Auth/SSO] New local account created for openvibe.tools user ${ssoUser.username} (openvibe-tools id:${openvibeToolsId}, local id:${localUser.id})`);
++ console.log(`[Auth/SSO] New local account created for openvibe.network user ${ssoUser.username} (openvibe-tools id:${openvibeToolsId}, local id:${localUser.id})`);
}
-- // Register this account under the user's openvibe.tools Linked Services.
++ // Register this account under the user's openvibe.network Linked Services.
try { require('../utils/notify').reportLinkedAccount(localUser); } catch { /* */ }
-- // Use the openvibe.tools token directly (no more local tokens)
++ // Use the openvibe.network token directly (no more local tokens)
const openvibeToolsToken = tokenData.access_token;
const openvibeRefreshToken = tokenData.refresh_token;
if (!openvibeToolsToken) {
@@@ -531,7 +531,7 @@ router.post('/logout', (req, res) => {
router.get('/sso/status', (req, res) => {
res.json({
enabled: !!OV_CLIENT_SECRET,
-- provider: 'openvibe.tools',
++ provider: 'openvibe.network',
loginUrl: `${getNetworkBase()}/oauth/authorize`,
});
});
ddiff --git a/server/chat/chat-server.js b/server/chat/chat-server.js
iindex 6a575359..cb0fc6d3 100644
---- a/server/chat/chat-server.js
++++ b/server/chat/chat-server.js
@@@ -55,7 +55,7 @@ class ChatServer {
this.wss = null;
/** @type {Map<WebSocket, { user: object|null, anonId: string, streamId: number|null, ip: string }>} */
this.clients = new Map();
-- /** @type {Map<string, number>} IP → unified anon number (warm cache, backed by openvibe.tools) */
++ /** @type {Map<string, number>} IP → unified anon number (warm cache, backed by openvibe.network) */
this.anonMap = new Map();
this.nextAnonId = 1;
this._anonDbLoaded = false;
@@@ -75,7 +75,7 @@ class ChatServer {
this.soundboardStreamLimits = new Map();
this._autoDeleteSweepInterval = null;
-- // ── Unified anon resolution via openvibe.tools internal API ──
++ // ── Unified anon resolution via openvibe.network internal API ──
this._openvibeToolsUrl = process.env.OV_NETWORK_INTERNAL_URL || 'http://127.0.0.1:3100';
this._internalKey = process.env.INTERNAL_API_KEY || process.env.OV_INTERNAL_KEY || '';
/** @type {Map<string, Promise<number>>} IP → pending resolve promise (dedup concurrent) */
@@@ -126,7 +126,7 @@ class ChatServer {
}
/**
-- * Resolve anon number from openvibe.tools unified API.
++ * Resolve anon number from openvibe.network unified API.
* Returns a Promise<number>. Caches in memory and falls back to local DB.
*/
async _resolveUnifiedAnonNum(ip) {
ddiff --git a/server/config.js b/server/config.js
iindex ed5d0e5b..6446adce 100644
---- a/server/config.js
++++ b/server/config.js
@@@ -1,7 +1,7 @@
/**
* OpenVibe.Live — Config
*
-- * Loads defaults from env, then resolves registry overrides from openvibe.tools
++ * Loads defaults from env, then resolves registry overrides from openvibe.network
* when available. Registry overrides take precedence over env values.
*/
require('dotenv').config();
@@@ -146,7 +146,7 @@ function buildConfig(registryValues) {
if (process.env.NODE_ENV === 'production' && baseUrl.includes('localhost')) {
console.error('[Config] CRITICAL: BASE_URL is localhost in production! CORS will reject all browser requests.');
-- console.error('[Config] CRITICAL: Set BASE_URL env var or configure it in the openvibe.tools admin registry.');
++ console.error('[Config] CRITICAL: Set BASE_URL env var or configure it in the openvibe.network admin registry.');
}
return {
@@@ -240,7 +240,7 @@ function buildConfig(registryValues) {
openvibeBucks: {
// Bit-style currency: integer bucks, 100 bucks = $1.00 streamer cashout
// (so 1 buck = $0.01 = one USD cent). Viewers buy at a premium with volume
-- // discounts (see openvibe-bucks.js pricing tiers) — the spread is the platform margin.
++ // discounts (see public/js/vibes.js pricing tiers) — the spread is the platform margin.
cashoutBucksPerUsd: 100,
minCashoutBucks: parseInt(process.env.MIN_CASHOUT_BUCKS || '500', 10), // $5.00
escrowDays: parseInt(process.env.ESCROW_HOLD_DAYS || '14', 10),
@@@ -300,12 +300,12 @@ async function refreshRegistry() {
},
});
if (!res.ok) {
-- console.warn('[Config] Failed to refresh URL registry from openvibe.tools:', res.status);
++ console.warn('[Config] Failed to refresh URL registry from openvibe.network:', res.status);
return config;
}
const body = await res.json();
if (!body.registry || typeof body.registry !== 'object') {
-- console.warn('[Config] Invalid registry response from openvibe.tools:', JSON.stringify(body).slice(0, 200));
++ console.warn('[Config] Invalid registry response from openvibe.network:', JSON.stringify(body).slice(0, 200));
return config;
}
@@@ -319,9 +319,9 @@ async function refreshRegistry() {
);
const updated = buildConfig(registry);
Object.assign(config, updated);
-- console.log('[Config] URL registry loaded from openvibe.tools resolved registry');
++ console.log('[Config] URL registry loaded from openvibe.network resolved registry');
} catch (err) {
-- console.warn('[Config] Unable to load URL registry from openvibe.tools:', err.message);
++ console.warn('[Config] Unable to load URL registry from openvibe.network:', err.message);
}
return config;
}
ddiff --git a/server/db/database.js b/server/db/database.js
iindex b5ea1d63..e9dd8494 100644
---- a/server/db/database.js
++++ b/server/db/database.js
@@@ -207,7 +207,7 @@ function initDb() {
database.exec(`CREATE INDEX IF NOT EXISTS idx_vkeys_status ON verification_keys(status)`);
} catch (e) { console.warn('[DB] verification_keys migration:', e.message); }
-- // Migrate: create linked_accounts table for openvibe.tools SSO
++ // Migrate: create linked_accounts table for openvibe.network SSO
try {
database.exec(`CREATE TABLE IF NOT EXISTS linked_accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@@ -812,7 +812,7 @@ function initDb() {
const seedGoogle = database.prepare("INSERT OR IGNORE INTO site_settings (key, value, description, type) VALUES (?, ?, ?, ?)");
for (const [k, v, d, t] of googleSeeds) seedGoogle.run(k, v, d, t);
-- // Seed payment-provider + monetization settings (configured in openvibe.tools/admin → Payments).
++ // Seed payment-provider + monetization settings (configured in openvibe.network/admin → Payments).
// Master switch is OFF by default so nothing goes live until an admin enables it.
const paymentSeeds = [
['payments_enabled', 'false', 'Master switch: enable real-money purchases & subscriptions', 'boolean'],
@@@ -870,7 +870,7 @@ function initDb() {
}
} catch (e) { console.warn('[DB] Vibes bit migration:', e.message); }
-- // AI analysis subsystem (configured in openvibe.tools/admin → AI). Master switch
++ // AI analysis subsystem (configured in openvibe.network/admin → AI). Master switch
// OFF by default so no API calls (or cost) happen until an admin enables it.
const aiSeeds = [
['ai_enabled', 'false', 'Master switch: enable AI analysis (pastes + stream memories)', 'boolean'],
ddiff --git a/server/index.js b/server/index.js
iindex 39a96cbc..fc3f5dfc 100644
---- a/server/index.js
++++ b/server/index.js
@@@ -126,16 +126,16 @@ function getAllowedOrigins() {
// Warn loudly when BASE_URL wasn't set and we're in production
if (process.env.NODE_ENV === 'production' && baseOrigin.includes('localhost')) {
console.error('[CORS] CRITICAL: config.baseUrl is localhost in production — CORS will reject all browser requests!');
-- console.error('[CORS] CRITICAL: Set BASE_URL in .env or configure it via the openvibe.tools admin URL registry.');
++ console.error('[CORS] CRITICAL: Set BASE_URL in .env or configure it via the openvibe.network admin URL registry.');
}
}
-- // Add the SSO provider origin (openvibe.tools) for OAuth callbacks and cross-domain API calls
++ // Add the SSO provider origin (openvibe.network) for OAuth callbacks and cross-domain API calls
const openvibeToolsOrigin = normalizeOrigin(config.openvibeToolsUrl || process.env.OV_NETWORK_URL);
if (openvibeToolsOrigin) {
allowed.add(openvibeToolsOrigin);
} else {
-- // OpenVibe default — allows openvibe.tools and admin panel to call this service
++ // OpenVibe default — allows openvibe.network and admin panel to call this service
allowed.add('https://openvibe.network');
}
@@@ -212,7 +212,7 @@ app.use(helmet({
crossOriginEmbedderPolicy: false,
}));
// The cross-site go-live widget + its SSE feed must be loadable from other OpenVibe
--// origins (openvibe.tools / openvibe.games), so relax CORP for just those two paths.
++// origins (openvibe.network / openvibe.games), so relax CORP for just those two paths.
app.use((req, res, next) => {
if (req.path === '/live-notify.js' || req.path === '/api/live-events') {
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
@@@ -923,7 +923,7 @@ async function start() {
console.log('[Server] Ready. Good vibes only. ▶');
console.log('');
-- // AI stream-memory job (no-op until enabled in openvibe.tools/admin → AI).
++ // AI stream-memory job (no-op until enabled in openvibe.network/admin → AI).
try { require('./ai/stream-memory-job').start(); } catch (e) { console.warn('[AI] memory job not started:', e.message); }
try { require('./ai/backfill-job').start(); } catch (e) { console.warn('[AI] backfill job not started:', e.message); }
try { require('./ai/streamer-overview-job').start(); } catch (e) { console.warn('[AI] streamer-overview job not started:', e.message); }
@@@ -1152,8 +1152,8 @@ async function start() {
}, 60000);
if (typeof maintenanceInterval.unref === 'function') maintenanceInterval.unref();
-- // 9. Periodic registry refresh — re-syncs config with openvibe.tools every 5 minutes.
-- // This is a safety net: if the startup refresh failed (openvibe.tools was temporarily
++ // 9. Periodic registry refresh — re-syncs config with openvibe.network every 5 minutes.
++ // This is a safety net: if the startup refresh failed (openvibe.network was temporarily
// unreachable), subsequent refreshes will fix CORS, issuer, and other URL config.
const registryRefreshInterval = setInterval(async () => {
try {
ddiff --git a/server/internal/routes.js b/server/internal/routes.js
iindex ef363644..a0166117 100644
---- a/server/internal/routes.js
++++ b/server/internal/routes.js
@@@ -26,10 +26,10 @@ router.post('/url-registry/refresh', async (req, res) => {
}
});
--// Authoritative role push from openvibe.tools (the SSO/role authority). Used so a
++// Authoritative role push from openvibe.network (the SSO/role authority). Used so a
// role change propagates to the local user record immediately — instead of
// waiting on the user's next token (up to 24h) and without letting a stale token
--// downgrade them. Matches by openvibe.tools account link first, then username.
++// downgrade them. Matches by openvibe.network account link first, then username.
router.post('/user-role', (req, res) => {
try {
const { username, openvibenetwork_id, role } = req.body || {};
ddiff --git a/server/monetization/payments.js b/server/monetization/payments.js
iindex d622cac7..bde8b81d 100644
---- a/server/monetization/payments.js
++++ b/server/monetization/payments.js
@@@ -3,7 +3,7 @@
*
* Real-money purchase of Vibes + channel subscriptions across PayPal,
* Stripe, CCBill and crypto (NOWPayments). All provider credentials live in
-- * site_settings (configured in openvibe.tools/admin → Payments). The master switch
++ * site_settings (configured in openvibe.network/admin → Payments). The master switch
* `payments_enabled` gates everything (default OFF) so nothing is live until an
* admin turns it on.
*
@@@ -27,7 +27,7 @@ function isEnabled() { return b('payments_enabled'); }
/** USD (dollars) → whole Vibes at the VALUE rate (100 bucks = $1). Used for
* converting real income (sub share, external tips) into bucks — NOT for purchase
-- * pricing (buying adds a margin; see openvibe-bucks.priceUsdForBucks). */
++ * pricing (buying adds a margin; see vibes.js priceUsdForBucks). */
function bucksForUsd(usd) {
const rate = n('bucks_per_usd', 100);
return Math.max(0, Math.round(usd * rate));
ddiff --git a/server/monetization/routes.js b/server/monetization/routes.js
iindex 61c66145..e8cdbb8b 100644
---- a/server/monetization/routes.js
++++ b/server/monetization/routes.js
@@@ -112,7 +112,7 @@ router.post('/donate', requireAuth, (req, res) => {
db.saveChatMessage({
stream_id: stream_id || null, channel_user_id: streamer_id, user_id: null,
username: 'Donation Goal',
-- message: `🎉 Goal reached: ${g.title} (${(g.target_amount || 0).toLocaleString()} HB)`,
++ message: `🎉 Goal reached: ${g.title} (${(g.target_amount || 0).toLocaleString()} Vibes)`,
message_type: 'donation',
metadata: { kind: 'goal-reached', goal_id: g.id, title: g.title, target: g.target_amount, image: g.image_url || null, media_type: g.media_type || null, by: donor },
});
ddiff --git a/server/streaming/routes.js b/server/streaming/routes.js
iindex 2ecd97e9..0b69e143 100644
---- a/server/streaming/routes.js
++++ b/server/streaming/routes.js
@@@ -81,9 +81,9 @@ const { notifyDiscordGoLive } = require('../integrations/discord-webhook');
const INTERNAL_API_KEY = config.internalApiKey || process.env.INTERNAL_API_KEY || process.env.OV_INTERNAL_KEY || '';
/**
-- * Push "X went live" notification via openvibe.tools unified event endpoint.
-- * This lets openvibe.tools handle Discord bot alerts + push notifications centrally.
-- * Falls back to direct webhook + bulk push if openvibe.tools is unreachable.
++ * Push "X went live" notification via openvibe.network unified event endpoint.
++ * This lets openvibe.network handle Discord bot alerts + push notifications centrally.
++ * Falls back to direct webhook + bulk push if openvibe.network is unreachable.
*/
function notifyFollowersGoLive(streamer, stream) {
// Try unified event endpoint first (handles Discord bot + push)
@@@ -119,7 +119,7 @@ function notifyFollowersGoLive(streamer, stream) {
});
}
--/** Fallback: direct Discord webhook + bulk push (if openvibe.tools is down) */
++/** Fallback: direct Discord webhook + bulk push (if openvibe.network is down) */
function _fallbackNotify(streamer, stream) {
notifyDiscordGoLive(streamer, stream);
ddiff --git a/server/utils/notify.js b/server/utils/notify.js
iindex 107e0113..0b9d2913 100644
---- a/server/utils/notify.js
++++ b/server/utils/notify.js
@@@ -30,7 +30,7 @@ function pushNotification(payload) {
});
}
--// Register this user as having a linked OpenVibe.Live account on openvibe.tools so it
++// Register this user as having a linked OpenVibe.Live account on openvibe.network so it
// appears under their Linked Services. Fire-and-forget + deduped per process.
const _linkedReported = new Set();
function reportLinkedAccount(user) {