openvibers/openvibe.live Commits
SPA repair: fix rename-broken identifiers, script refs (vibes/opencoins), OpenVibe.Live branding; proxy storage tier admin to Media
Changes
@@@ -144,7 +144,7 @@ class HardwareController:# TTSdef 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)@@@ -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 & Copyright Policy</span>@@@ -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" alerts6. 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 — <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) — no browser login needed.</p>++<p><strong>All identity is OpenVibe SSO.</strong> There is no local password login on OpenVibe.Live — <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) — no browser login needed.</p><h3>How auth is sent</h3><p>Every authenticated request carries a token as <code>Authorization: Bearer <token></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> — 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> — from the SSO login flow; auto-links/creates a local account. Roles come from the token.</li>++<li><strong>openvibe.network JWT (RS256)</strong> — 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> > <code>global_mod</code> > <code>streamer</code> > <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 → openvibe.tools OAuth authorize</td></tr>++<tr><td>GET</td><td>/api/auth/sso/login</td><td>No</td><td>302 → openvibe.network OAuth authorize</td></tr><tr><td>GET</td><td>/api/auth/callback?code&state</td><td>No</td><td>OAuth code→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 · /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 · /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 · /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 bellconst 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)@@@ -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>@@@ -12,7 +12,7 @@ let _streamSwitchToken = 0; // increments each stream switch; guards against faslet 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 timerstartTokenRefreshTimer();-- // 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@@@ -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">@@@ -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>`@@@ -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>`;}@@@ -3129,7 +3129,6 @@ function interactWithNPC(npc) {}panelAnimProgress = 0;panelAnimDir = 1;-- }loadNPCData(npc.id);}@@@ -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.networkconst keyRes = await apiFetch('/api/push/vapid-key');if (!keyRes?.publicKey) return { error: 'Push not configured on server' };@@@ -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;}@@@ -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.networkfunction changePassword() {-- window.open(getOpenVibe.ToolsAdminUrl(), '_blank');++ window.open(getOpenVibeToolsAdminUrl(), '_blank');}/* ── Broadcaster Tab ──────────────────────────────────────────── */@@@ -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>@@@ -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';@@@ -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>@@@ -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>@@@ -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() }));}// ═══════════════════════════════════════════════════════════════@@@ -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.*/@@@ -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');@@@ -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 accounttry {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 accounttry {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 JWTconst 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();@@@ -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 accountconst 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.networkif (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.networkdb.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`,});});@@@ -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) {@@@ -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.00escrowDays: 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;}@@@ -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 SSOtry {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'],@@@ -126,16 +126,16 @@ function getAllowedOrigins() {// Warn loudly when BASE_URL wasn't set and we're in productionif (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 callsconst 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 serviceallowed.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 {@@@ -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 || {};@@@ -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));@@@ -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 },});@@@ -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);@@@ -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) {
@@ -144,7 +144,7 @@ class HardwareController:
@@ -144,7 +144,7 @@ class HardwareController:
# TTS
# TTS
def cmd_tts(self, data=None):
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:
try:
subprocess.Popen(["espeak", "-s", "150", text],
subprocess.Popen(["espeak", "-s", "150", text],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
@@ -201,7 +201,7 @@
@@ -201,7 +201,7 @@
<header class="topbar">
<header class="topbar">
<a class="topbar-brand" href="https://openvibe.live">
<a class="topbar-brand" href="https://openvibe.live">
<i class="fa-solid fa-circle-nodes"></i>
<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>
</a>
<span class="topbar-sep">/</span>
<span class="topbar-sep">/</span>
<span class="topbar-page">DMCA & Copyright Policy</span>
<span class="topbar-page">DMCA & Copyright Policy</span>
@@ -89,7 +89,7 @@
@@ -89,7 +89,7 @@
<i class="fa-solid fa-circle-nodes"></i>
<i class="fa-solid fa-circle-nodes"></i>
<span class="brand-text-wrap">
<span class="brand-text-wrap">
<span class="alpha-badge">ALPHA</span>
<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>
</span>
</a>
</a>
<div class="nav-links-wrap">
<div class="nav-links-wrap">
@@ -121,7 +121,7 @@
@@ -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>
<a href="/dashboard" id="nav-golive-dash" onclick="return dashNav(event)"><i class="fa-solid fa-gauge-high"></i> Dashboard</a>
</div>
</div>
</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>
<i class="fa-solid fa-shield-halved"></i> <span>Admin</span>
</a>
</a>
</div>
</div>
@@ -149,7 +149,7 @@
@@ -149,7 +149,7 @@
<div class="user-dropdown" id="user-dropdown">
<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="/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="#" 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>
<hr>
<a href="https://openvibe.network" target="_blank"><i class="fa-solid fa-network-wired"></i> OpenVibe</a>
<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>
<a href="#" onclick="event.preventDefault();showModal('buy-funds')"><i class="fa-solid fa-coins"></i> Support Us</a>
@@ -1811,7 +1811,7 @@
@@ -1811,7 +1811,7 @@
</div>
</div>
<div style="display:flex;gap:10px;align-items:center;margin-top:14px;flex-wrap:wrap">
<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>
<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>
</div>
</div>
@@ -3493,7 +3493,7 @@ WantedBy=multi-user.target</code>
@@ -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>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>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>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>
</tbody>
</table>
</table>
@@ -3519,7 +3519,7 @@ WantedBy=multi-user.target</code>
@@ -3519,7 +3519,7 @@ WantedBy=multi-user.target</code>
<thead><tr><th>Service</th><th>URL</th><th>Owns</th></tr></thead>
<thead><tr><th>Service</th><th>URL</th><th>Owns</th></tr></thead>
<tbody>
<tbody>
<tr><td>OpenVibe.Live</td><td>openvibe.live</td><td>Streaming, chat, TTS, controls, VODs, soundboard</td></tr>
<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>
<tr><td>openvibe.games</td><td>openvibe.games</td><td>Games (RPG, canvas)</td></tr>
</tbody>
</tbody>
</table>
</table>
@@ -3538,7 +3538,7 @@ WantedBy=multi-user.target</code>
@@ -3538,7 +3538,7 @@ WantedBy=multi-user.target</code>
<h3>Service Map</h3>
<h3>Service Map</h3>
<pre><code>┌──────────────────────────────────────────────────────┐
<pre><code>┌──────────────────────────────────────────────────────┐
-│ openvibe.tools (port 3100) — Central Hub │
+│ openvibe.network (port 3100) — Central Hub │
│ ├─ /api/auth — SSO (RS256 JWT) │
│ ├─ /api/auth — SSO (RS256 JWT) │
│ ├─ /api/notifications — Notification CRUD │
│ ├─ /api/notifications — Notification CRUD │
│ ├─ /api/push — Web Push (VAPID) │
│ ├─ /api/push — Web Push (VAPID) │
@@ -3562,13 +3562,13 @@ WantedBy=multi-user.target</code>
@@ -3562,13 +3562,13 @@ WantedBy=multi-user.target</code>
<h3>Event Flow: Stream Goes Live</h3>
<h3>Event Flow: Stream Goes Live</h3>
<pre><code>1. Streamer calls POST /api/streams (OpenVibe.Live)
<pre><code>1. Streamer calls POST /api/streams (OpenVibe.Live)
2. OpenVibe.Live creates stream record, starts media endpoint
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
- All followers of the streamer
- Users opted into "all live" alerts
- Users opted into "all live" alerts
6. Push notifications fire via Web Push (VAPID)
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>
<h3>WebSocket Services</h3>
<table class="doc-table">
<table class="doc-table">
@@ -3582,10 +3582,10 @@ WantedBy=multi-user.target</code>
@@ -3582,10 +3582,10 @@ WantedBy=multi-user.target</code>
</table>
</table>
<h3>Admin Proxy Architecture</h3>
<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>
<p>Protected by X-Internal-Key header for server-to-server calls.</p>
</div>
</div>
@@ -3595,13 +3595,13 @@ openvibe.tools /api/admin/streamer-mod/* → OpenVibe.Live /api/mod/*</code></
@@ -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">
<div class="doc-tab-content" data-doc-tab="auth" style="display:none">
<h2>1. AUTHENTICATION</h2>
<h2>1. AUTHENTICATION</h2>
-<p><strong>All identity is OpenVibe.Tools SSO.</strong> There is no local password login on OpenVibe.Live — <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) — no browser login needed.</p>
+<p><strong>All identity is OpenVibe SSO.</strong> There is no local password login on OpenVibe.Live — <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) — no browser login needed.</p>
<h3>How auth is sent</h3>
<h3>How auth is sent</h3>
<p>Every authenticated request carries a token as <code>Authorization: Bearer <token></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>
<p>Every authenticated request carries a token as <code>Authorization: Bearer <token></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>
<ul>
<li><strong>API token</strong> — a string starting with <code>hbt_</code>. Best for bots/integrations. Cannot itself create/list/revoke tokens.</li>
<li><strong>API token</strong> — 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> — from the SSO login flow; auto-links/creates a local account. Roles come from the token.</li>
+<li><strong>openvibe.network JWT (RS256)</strong> — from the SSO login flow; auto-links/creates a local account. Roles come from the token.</li>
</ul>
</ul>
<p><strong>Roles:</strong> <code>admin</code> > <code>global_mod</code> > <code>streamer</code> > <code>user</code> (+ anonymous). Banned accounts get <code>403</code>.</p>
<p><strong>Roles:</strong> <code>admin</code> > <code>global_mod</code> > <code>streamer</code> > <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></
@@ -3609,7 +3609,7 @@ openvibe.tools /api/admin/streamer-mod/* → OpenVibe.Live /api/mod/*</code></
<table class="doc-table">
<table class="doc-table">
<thead><tr><th>Method</th><th>Path</th><th>Auth</th><th>Notes</th></tr></thead>
<thead><tr><th>Method</th><th>Path</th><th>Auth</th><th>Notes</th></tr></thead>
<tbody>
<tbody>
-<tr><td>GET</td><td>/api/auth/sso/login</td><td>No</td><td>302 → openvibe.tools OAuth authorize</td></tr>
+<tr><td>GET</td><td>/api/auth/sso/login</td><td>No</td><td>302 → openvibe.network OAuth authorize</td></tr>
<tr><td>GET</td><td>/api/auth/callback?code&state</td><td>No</td><td>OAuth code→token exchange; sets cookies</td></tr>
<tr><td>GET</td><td>/api/auth/callback?code&state</td><td>No</td><td>OAuth code→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/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>
<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>
@@ -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">
<div class="doc-tab-content" data-doc-tab="integrations" style="display:none">
<h2>INTEGRATIONS — DISCORD, PUSH, WEBHOOKS</h2>
<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">
<table class="doc-table">
<thead><tr><th>Feature</th><th>Description</th></tr></thead>
<thead><tr><th>Feature</th><th>Description</th></tr></thead>
<tbody>
<tbody>
@@ -4457,7 +4457,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
@@ -4457,7 +4457,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
</tbody>
</tbody>
</table>
</table>
-<h3>Discord Admin API (openvibe.tools)</h3>
+<h3>Discord Admin API (openvibe.network)</h3>
<table class="doc-table">
<table class="doc-table">
<thead><tr><th>Method</th><th>Path</th><th>Description</th></tr></thead>
<thead><tr><th>Method</th><th>Path</th><th>Description</th></tr></thead>
<tbody>
<tbody>
@@ -4503,7 +4503,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
@@ -4503,7 +4503,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
</table>
</table>
<h3>Internal Events API (server-to-server)</h3>
<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">
<table class="doc-table">
<thead><tr><th>Method</th><th>Path</th><th>Description</th></tr></thead>
<thead><tr><th>Method</th><th>Path</th><th>Description</th></tr></thead>
<tbody>
<tbody>
@@ -4514,7 +4514,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
@@ -4514,7 +4514,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
</table>
</table>
<h3>Fallback Webhook (OpenVibe.Live direct)</h3>
<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>
<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>
<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>
@@ -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 / 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/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/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 · /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 · /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/meta/link-preview?url=</td><td>No</td><td>Link preview / OEmbed data</td></tr>
<tr><td>GET</td><td>/api/health · /updates</td><td>No</td><td>Health check / recent changelog (git commits)</td></tr>
<tr><td>GET</td><td>/api/health · /updates</td><td>No</td><td>Health check / recent changelog (git commits)</td></tr>
</tbody>
</tbody>
@@ -4904,18 +4904,18 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
@@ -4904,18 +4904,18 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
<!-- Scripts -->
<!-- Scripts -->
<script src="/js/theme-engine.js?v=4"></script>
<script src="/js/theme-engine.js?v=4"></script>
<script src="/live-notify.js?v=2" defer></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/stream-player.js?v=17" defer></script>
<script src="/js/chat-shared.js?v=3" defer></script>
<script src="/js/chat-shared.js?v=3" defer></script>
<script src="/js/emotes.js?v=8" defer></script>
<script src="/js/emotes.js?v=8" defer></script>
<script src="/js/gif-picker.js?v=1" 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/chat-autocomplete.js?v=1" defer></script>
<script src="/js/channel-uploads.js?v=12" defer></script>
<script src="/js/channel-uploads.js?v=12" defer></script>
<script src="/js/controls.js?v=8" defer></script>
<script src="/js/controls.js?v=8" defer></script>
<script src="/js/onvif-dashboard.js?v=5" 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) ── -->
<!-- ── Floating Global Chat Widget (visible on non-chat pages) ── -->
<div id="floating-chat-widget" class="floating-chat-widget" style="display:none">
<div id="floating-chat-widget" class="floating-chat-widget" style="display:none">
<div class="fcw-header" onmousedown="fcwStartDrag(event)">
<div class="fcw-header" onmousedown="fcwStartDrag(event)">
@@ -4959,8 +4959,8 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
@@ -4959,8 +4959,8 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
</div>
</div>
</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>
<script src="/js/cosmetics-ui.js?v=2" defer></script>
<!-- Broadcast modules — load order matters: state → devices → vods → workspace → core -->
<!-- Broadcast modules — load order matters: state → devices → vods → workspace → core -->
<script src="/js/broadcast-state.js?v=1" defer></script>
<script src="/js/broadcast-state.js?v=1" defer></script>
@@ -4970,7 +4970,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
@@ -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/broadcast.js?v=25" defer></script>
<script src="/js/call.js?v=3" defer></script>
<script src="/js/call.js?v=3" defer></script>
<script src="/js/voice-channels.js?v=2" 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/staff-console.js?v=1" defer></script>
<script src="/js/dashboard-moderation.js?v=5" defer></script>
<script src="/js/dashboard-moderation.js?v=5" defer></script>
<script src="/js/ai-viewers.js?v=1" 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>
@@ -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).
// Initialize OpenVibe notifications and theme sync (navbar is handled natively by openvibelive).
// Runs on DOMContentLoaded so the now-deferred shared libs (OpenVibeThemeLoader / OpenVibeNotifications)
// Runs on DOMContentLoaded so the now-deferred shared libs (OpenVibeThemeLoader / OpenVibeNotifications)
// have executed first.
// 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;
const token = localStorage.getItem('token') || (document.cookie.match(/(?:^|; )ov_token=([^;]*)/) || [])[1] || null;
if (window.OpenVibeThemeLoader && typeof OpenVibeThemeLoader.init === 'function') {
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).
// user's active theme from there (configured at openvibe.network/themes).
OpenVibeThemeLoader.init({ apiBase: 'https://openvibe.network' });
OpenVibeThemeLoader.init({ apiBase: 'https://openvibe.network' });
}
}
@@ -4999,7 +4999,7 @@ document.addEventListener('DOMContentLoaded', function initNet.OpenVibework() {
@@ -4999,7 +4999,7 @@ document.addEventListener('DOMContentLoaded', function initNet.OpenVibework() {
// Notifications bell
// Notifications bell
const mount = document.getElementById('openvibe-bell-mount');
const mount = document.getElementById('openvibe-bell-mount');
if (mount && window.OpenVibeNotifications && token) {
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)
|| (['localhost', '127.0.0.1'].includes(window.location.hostname)
? 'http://localhost:3100'
? 'http://localhost:3100'
: (['topenvibe.tools', 'topenvibelive.com', 'topenvibe.quest'].includes(window.location.hostname)
: (['topenvibe.tools', 'topenvibelive.com', 'topenvibe.quest'].includes(window.location.hostname)
@@ -770,7 +770,7 @@ async function loadAdminCashouts() {
@@ -770,7 +770,7 @@ async function loadAdminCashouts() {
<tbody>${cashouts.map(co => `
<tbody>${cashouts.map(co => `
<tr>
<tr>
<td>${esc(co.username || '-')}</td>
<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>$${(co.amount * 0.01).toFixed(2)}</td>
<td>${esc(co.paypal_email || '-')}</td>
<td>${esc(co.paypal_email || '-')}</td>
<td>${new Date(co.created_at).toLocaleString()}</td>
<td>${new Date(co.created_at).toLocaleString()}</td>
@@ -12,7 +12,7 @@ let _streamSwitchToken = 0; // increments each stream switch; guards against fas
@@ -12,7 +12,7 @@ let _streamSwitchToken = 0; // increments each stream switch; guards against fas
let openvibeAppMetaData = null;
let openvibeAppMetaData = null;
let openvibeAppMetaPromise = null;
let openvibeAppMetaPromise = null;
-function getDefaultNet.OpenVibeworkUrls() {
+function getDefaultOpenVibeNetworkUrls() {
const host = window.location.hostname;
const host = window.location.hostname;
const isLocalHost = ['localhost', '127.0.0.1'].includes(host);
const isLocalHost = ['localhost', '127.0.0.1'].includes(host);
const isTopenvibeAlias = ['topenvibe.tools', 'topenvibelive.com', 'topenvibe.quest'].includes(host);
const isTopenvibeAlias = ['topenvibe.tools', 'topenvibelive.com', 'topenvibe.quest'].includes(host);
@@ -22,17 +22,17 @@ function getDefaultNet.OpenVibeworkUrls() {
@@ -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() {
function getScraplandiaUrl() {
- return getNet.OpenVibeworkUrl('quest');
+ return getOpenVibeNetworkUrl('quest');
}
}
/** Cached external viewer count (Kick/Twitch/RS) — updated by cumulative viewer poll */
/** Cached external viewer count (Kick/Twitch/RS) — updated by cumulative viewer poll */
let _cachedExternalViewerCount = 0;
let _cachedExternalViewerCount = 0;
@@ -636,7 +636,7 @@ function showModal(id) {
@@ -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">
<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
<i class="fa-solid fa-network-wired"></i> Sign in with OpenVibe
</a>
</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(),
donate: openvibeBucksDonateModal(),
'buy-funds': openvibeBucksBuyModal(),
'buy-funds': openvibeBucksBuyModal(),
cashout: openvibeBucksCashoutModal(),
cashout: openvibeBucksCashoutModal(),
@@ -939,10 +939,10 @@ function routeFromURL() {
@@ -939,10 +939,10 @@ function routeFromURL() {
showPage('broadcast');
showPage('broadcast');
loadBroadcastPage();
loadBroadcastPage();
} else if (segments[0] === 'admin') {
} else if (segments[0] === 'admin') {
- window.location.href = `${getOpenVibe.ToolsUrl()}/admin`;
+ window.location.href = `${getOpenVibeToolsUrl()}/admin`;
return;
return;
} else if (segments[0] === 'themes') {
} 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';
window.location.href = 'https://openvibe.network/themes';
return;
return;
} else if (segments[0] === 'chat') {
} else if (segments[0] === 'chat') {
@@ -4892,7 +4892,7 @@ function _goalWidgetItemHTML(g) {
@@ -4892,7 +4892,7 @@ function _goalWidgetItemHTML(g) {
<div class="cgw-body">
<div class="cgw-body">
<div class="cgw-top">
<div class="cgw-top">
<span class="cgw-title">${reached ? '🎉 ' : ''}${esc(g.title)}</span>
<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>
<span class="cgw-pct">${reached ? '✓' : pct + '%'}</span>
</div>
</div>
</div>
</div>
@@ -8013,8 +8013,8 @@ document.addEventListener('DOMContentLoaded', async () => {
@@ -8013,8 +8013,8 @@ document.addEventListener('DOMContentLoaded', async () => {
// Start proactive token refresh timer
// Start proactive token refresh timer
startTokenRefreshTimer();
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.
// 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
// Route from current URL instead of always going home — but not in the popout
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
/* ═══════════════════════════════════════════════════════════════
/* ═══════════════════════════════════════════════════════════════
OpenVibe.Live — Vibes (Virtual Currency UI)
OpenVibe.Live — Vibes (Virtual Currency UI)
- 1 OpenVibe Buck = $1.00 USD
+ 1 Vibe = $1.00 USD
═══════════════════════════════════════════════════════════════ */
═══════════════════════════════════════════════════════════════ */
/**
/**
@@ -9,17 +9,17 @@
@@ -9,17 +9,17 @@
function openvibeBucksBuyModal() {
function openvibeBucksBuyModal() {
return `
return `
<h3><i class="fa-solid fa-coins"></i> Buy Vibes</h3>
<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">
<div class="form-group">
<label>Amount</label>
<label>Amount</label>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:12px">
<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>
</div>
</div>
@@ -81,12 +81,12 @@ function openvibeBucksDonateModal() {
@@ -81,12 +81,12 @@ function openvibeBucksDonateModal() {
<p class="muted" style="margin-bottom:16px">Support this streamer!</p>
<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">
<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>
<div class="form-group">
<div class="form-group">
@@ -3288,7 +3288,7 @@ function addDonationGoalMessage(msg) {
@@ -3288,7 +3288,7 @@ function addDonationGoalMessage(msg) {
const g = msg.goal || {};
const g = msg.goal || {};
const by = esc(msg.by || 'the community');
const by = esc(msg.by || 'the community');
const title = esc(g.title || 'Goal');
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
const media = g.image_url
? (g.media_type === 'video'
? (g.media_type === 'video'
? `<video class="cgr-media" src="${esc(g.image_url)}" muted loop autoplay playsinline></video>`
? `<video class="cgr-media" src="${esc(g.image_url)}" muted loop autoplay playsinline></video>`
@@ -978,7 +978,7 @@ async function loadDashFunds() {
@@ -978,7 +978,7 @@ async function loadDashFunds() {
const set = (id, v) => { const el = document.getElementById(id); if (el) el.textContent = v; };
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).
// Integer Vibes; USD shown is the cashout value (100 bucks = $1).
set('dash-funds-amount', bal.toLocaleString());
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-amount', cash.toLocaleString());
set('dash-cashout-usd', `($${(cash / 100).toFixed(2)})`);
set('dash-cashout-usd', `($${(cash / 100).toFixed(2)})`);
} catch { /* silent */ }
} catch { /* silent */ }
@@ -1001,7 +1001,7 @@ function _bucksTxRow(t) {
@@ -1001,7 +1001,7 @@ function _bucksTxRow(t) {
return `<div class="bucks-log-row">
return `<div class="bucks-log-row">
<div class="bucks-log-icon"><i class="fa-solid ${icon}"></i></div>
<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-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>`;
</div>`;
}
}
@@ -3129,7 +3129,6 @@ function interactWithNPC(npc) {
@@ -3129,7 +3129,6 @@ function interactWithNPC(npc) {
}
}
panelAnimProgress = 0;
panelAnimProgress = 0;
panelAnimDir = 1;
panelAnimDir = 1;
- }
loadNPCData(npc.id);
loadNPCData(npc.id);
}
}
@@ -14,7 +14,7 @@
@@ -14,7 +14,7 @@
const MAX_TOASTS = 3;
const MAX_TOASTS = 3;
const SOUNDS = { normal: 'notification.mp3', high: 'notification-high.mp3', critical: 'notification-alarm.mp3' };
const SOUNDS = { normal: 'notification.mp3', high: 'notification-high.mp3', critical: 'notification-alarm.mp3' };
- function getDefaultOpenVibe.ToolsUrl() {
+ function getDefaultOpenVibeToolsUrl() {
const host = window.location.hostname;
const host = window.location.hostname;
const isLocalHost = ['localhost', '127.0.0.1'].includes(host);
const isLocalHost = ['localhost', '127.0.0.1'].includes(host);
const isTopenvibeAlias = ['topenvibe.tools', 'topenvibelive.com', 'topenvibe.quest'].includes(host);
const isTopenvibeAlias = ['topenvibe.tools', 'topenvibelive.com', 'topenvibe.quest'].includes(host);
@@ -23,7 +23,7 @@
@@ -23,7 +23,7 @@
let _config = {
let _config = {
token: null,
token: null,
- apiBase: (window.Net.OpenVibeworkUrls && window.Net.OpenVibeworkUrls.tools) || getDefaultOpenVibe.ToolsUrl(),
+ apiBase: (window.OpenVibeNetworkUrls && window.OpenVibeNetworkUrls.tools) || getDefaultOpenVibeToolsUrl(),
soundBase: '/assets/sounds',
soundBase: '/assets/sounds',
onAction: null,
onAction: null,
};
};
@@ -568,7 +568,7 @@
@@ -568,7 +568,7 @@
if (!('serviceWorker' in navigator) || !('PushManager' in window)) return { error: 'Push not supported' };
if (!('serviceWorker' in navigator) || !('PushManager' in window)) return { error: 'Push not supported' };
try {
try {
const reg = await navigator.serviceWorker.register('/service-worker.js');
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');
const keyRes = await apiFetch('/api/push/vapid-key');
if (!keyRes?.publicKey) return { error: 'Push not configured on server' };
if (!keyRes?.publicKey) return { error: 'Push not configured on server' };
@@ -232,7 +232,7 @@ function handleCoinEarned(msg) {
@@ -232,7 +232,7 @@ function handleCoinEarned(msg) {
function renderRedemption(msg, container) {
function renderRedemption(msg, container) {
const el = document.createElement('div');
const el = document.createElement('div');
el.className = 'chat-msg redemption';
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.appendChild(el);
container.scrollTop = container.scrollHeight;
container.scrollTop = container.scrollHeight;
}
}
@@ -198,7 +198,7 @@ async function saveSettingsProfile() {
@@ -198,7 +198,7 @@ async function saveSettingsProfile() {
email: document.getElementById('set-email').value.trim() || null,
email: document.getElementById('set-email').value.trim() || null,
bio: document.getElementById('set-bio').value.trim(),
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.
// local value on every login) — only send it if the field is still present.
const _pc = document.getElementById('set-profile-color');
const _pc = document.getElementById('set-profile-color');
if (_pc) data.profile_color = _pc.value;
if (_pc) data.profile_color = _pc.value;
@@ -215,20 +215,20 @@ async function saveSettingsProfile() {
@@ -215,20 +215,20 @@ async function saveSettingsProfile() {
}
}
}
}
-function getDefaultOpenVibe.ToolsUrl() {
+function getDefaultOpenVibeToolsUrl() {
const host = window.location.hostname;
const host = window.location.hostname;
const isLocalHost = ['localhost', '127.0.0.1'].includes(host);
const isLocalHost = ['localhost', '127.0.0.1'].includes(host);
const isTopenvibeAlias = ['topenvibe.tools', 'topenvibelive.com', 'topenvibe.quest'].includes(host);
const isTopenvibeAlias = ['topenvibe.tools', 'topenvibelive.com', 'topenvibe.quest'].includes(host);
return isLocalHost ? 'http://localhost:3100' : (isTopenvibeAlias ? 'https://topenvibe.tools' : 'https://openvibe.network');
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 {
try {
const u = new URL(url);
const u = new URL(url);
u.hostname = u.hostname.replace(/^www\./, 'my.');
u.hostname = u.hostname.replace(/^www\./, 'my.');
@@ -238,9 +238,9 @@ function getOpenVibe.ToolsAdminUrl() {
@@ -238,9 +238,9 @@ function getOpenVibe.ToolsAdminUrl() {
}
}
}
}
-// Password management handled on openvibe.tools
+// Password management handled on openvibe.network
function changePassword() {
function changePassword() {
- window.open(getOpenVibe.ToolsAdminUrl(), '_blank');
+ window.open(getOpenVibeToolsAdminUrl(), '_blank');
}
}
/* ── Broadcaster Tab ──────────────────────────────────────────── */
/* ── Broadcaster Tab ──────────────────────────────────────────── */
@@ -4,7 +4,7 @@
@@ -4,7 +4,7 @@
═══════════════════════════════════════════════════════════════ */
═══════════════════════════════════════════════════════════════ */
// Client mirror of the server volume-discount tiers (server is the source of truth;
// 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]];
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) {
function _priceForBucks(b) {
b = Math.max(0, Math.round(Number(b) || 0));
b = Math.max(0, Math.round(Number(b) || 0));
@@ -56,7 +56,7 @@ async function _initBuyBucks() {
@@ -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 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');
const pkgBox = document.getElementById('buy-packages');
if (pkgBox) pkgBox.innerHTML = packages.map(p =>
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('');
).join('');
const min = cfg.minPurchaseBucks || 100;
const min = cfg.minPurchaseBucks || 100;
@@ -160,12 +160,12 @@ function openvibeBucksDonateModal() {
@@ -160,12 +160,12 @@ function openvibeBucksDonateModal() {
<p class="muted" style="margin-bottom:16px">Support this streamer!</p>
<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">
<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>
<div class="form-group">
<div class="form-group">
@@ -206,7 +206,7 @@ async function _loadDonateGoals() {
@@ -206,7 +206,7 @@ async function _loadDonateGoals() {
const goals = (data.goals || []).filter(g => g.is_active);
const goals = (data.goals || []).filter(g => g.is_active);
if (!goals.length) { wrap.style.display = 'none'; return; }
if (!goals.length) { wrap.style.display = 'none'; return; }
sel.innerHTML = `<option value="">General support (no specific goal)</option>` +
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 = '';
wrap.style.display = '';
// Pre-select a goal if the donate modal was opened from a goal popover.
// Pre-select a goal if the donate modal was opened from a goal popover.
if (window._pendingDonateGoalId) {
if (window._pendingDonateGoalId) {
@@ -251,7 +251,7 @@ async function doDonate() {
@@ -251,7 +251,7 @@ async function doDonate() {
function openvibeBucksCashoutModal() {
function openvibeBucksCashoutModal() {
return `
return `
<h3><i class="fa-solid fa-money-bill-transfer"></i> Cash Out</h3>
<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">
<div class="form-group">
<label>Amount (Vibes)</label>
<label>Amount (Vibes)</label>
@@ -262,14 +262,14 @@
@@ -262,14 +262,14 @@
<!-- Chat feature scripts — mirror index.html so the popout has full parity.
<!-- Chat feature scripts — mirror index.html so the popout has full parity.
Order: dependencies (app/emotes/gif/uploads/coins/bucks/cosmetics) before chat.js. -->
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/emotes.js?v=8"></script>
<script src="/js/gif-picker.js?v=1"></script>
<script src="/js/gif-picker.js?v=1"></script>
<script src="/js/channel-uploads.js?v=6"></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/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 src="/js/chat-autocomplete.js?v=1"></script>
<script>
<script>
'use strict';
'use strict';
@@ -197,7 +197,7 @@
@@ -197,7 +197,7 @@
<header class="topbar">
<header class="topbar">
<a class="topbar-brand" href="https://openvibe.live">
<a class="topbar-brand" href="https://openvibe.live">
<i class="fa-solid fa-circle-nodes"></i>
<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>
</a>
<span class="topbar-sep">/</span>
<span class="topbar-sep">/</span>
<span class="topbar-page">Privacy Policy</span>
<span class="topbar-page">Privacy Policy</span>
@@ -217,7 +217,7 @@
@@ -217,7 +217,7 @@
<h2><i class="fa-solid fa-box-archive"></i> What We Collect</h2>
<h2><i class="fa-solid fa-box-archive"></i> What We Collect</h2>
<p>Only what's needed to run a streaming platform:</p>
<p>Only what's needed to run a streaming platform:</p>
<ul class="rule-list">
<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-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-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>
<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>
@@ -197,7 +197,7 @@
@@ -197,7 +197,7 @@
<header class="topbar">
<header class="topbar">
<a class="topbar-brand" href="https://openvibe.live">
<a class="topbar-brand" href="https://openvibe.live">
<i class="fa-solid fa-circle-nodes"></i>
<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>
</a>
<span class="topbar-sep">/</span>
<span class="topbar-sep">/</span>
<span class="topbar-page">Terms of Service</span>
<span class="topbar-page">Terms of Service</span>
@@ -449,7 +449,7 @@ router.get('/settings', (req, res) => {
@@ -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) => {
router.get('/ai/usage', (req, res) => {
try {
try {
const days = Math.min(365, Math.max(1, parseInt(req.query.days, 10) || 30));
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) => {
@@ -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() }));
}
}
// ═══════════════════════════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════
@@ -4,7 +4,7 @@
@@ -4,7 +4,7 @@
* Vision + text analysis used for: paste description/tags, paste text overviews,
* Vision + text analysis used for: paste description/tags, paste text overviews,
* and periodic live-stream "memories". Provider (Anthropic Claude by default, or
* and periodic live-stream "memories". Provider (Anthropic Claude by default, or
* any OpenAI-compatible endpoint) + key + model are configured in
* 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
* an optional daily USD budget cap. Token usage + estimated cost are recorded to
* `ai_usage` for the admin cost breakdown.
* `ai_usage` for the admin cost breakdown.
*/
*/
@@ -7,7 +7,7 @@
@@ -7,7 +7,7 @@
* - A "decent" overview (>= DECENT_LEN chars) is refreshed at most once per 12h.
* - A "decent" overview (>= DECENT_LEN chars) is refreshed at most once per 12h.
* - A sparse/missing overview is retried hourly until it fills out.
* - 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
* 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';
'use strict';
const db = require('../db/database');
const db = require('../db/database');
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
/**
/**
* OpenVibe.Live — JWT Auth Middleware
* OpenVibe.Live — JWT Auth Middleware
* All authentication is handled via OpenVibe.Tools RS256 tokens.
* 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.
* Local user records are resolved via linked_accounts.
*/
*/
const jwt = require('jsonwebtoken');
const jwt = require('jsonwebtoken');
@@ -13,7 +13,7 @@ const db = require('../db/database');
@@ -13,7 +13,7 @@ const db = require('../db/database');
// ── OpenVibe.Tools Public Key (RS256 verification) ──────────────
// ── OpenVibe.Tools Public Key (RS256 verification) ──────────────
let openvibeToolsPublicKey = null;
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
// 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.
// at verify-time so config.refreshRegistry() updates are picked up automatically.
const config = require('../config');
const config = require('../config');
@@ -32,17 +32,17 @@ function loadNetworkPublicKey() {
@@ -32,17 +32,17 @@ function loadNetworkPublicKey() {
try {
try {
if (fs.existsSync(p)) {
if (fs.existsSync(p)) {
openvibeToolsPublicKey = fs.readFileSync(p, 'utf8');
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;
return;
}
}
} catch { /* try next */ }
} 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();
loadNetworkPublicKey();
/**
/**
- * Verify a openvibe.tools RS256 JWT token.
+ * Verify a openvibe.network RS256 JWT token.
* Returns decoded payload or null.
* Returns decoded payload or null.
*/
*/
function verifyToken(token) {
function verifyToken(token) {
@@ -75,13 +75,13 @@ function verifyTokenWithReason(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,
* Checks linked_accounts first, falls back to username match,
* auto-creates a local account if none found.
* 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
// 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.
// `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 };
const _ROLE_RANK = { user: 0, streamer: 1, global_mod: 2, admin: 3 };
@@ -95,7 +95,7 @@ function _syncSsoUserFields(user, decoded) {
@@ -95,7 +95,7 @@ function _syncSsoUserFields(user, decoded) {
// live 24h, so a just-promoted admin's older token still carries role:'user'
// 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.
// and would otherwise strip their role (and Staff badge) on every connect.
// Upgrades apply immediately; downgrades are pushed authoritatively from
// 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
if (decoded.role && _ROLE_RANK[decoded.role] !== undefined && decoded.role !== user.role
&& _ROLE_RANK[decoded.role] > (_ROLE_RANK[user.role] ?? 0)) {
&& _ROLE_RANK[decoded.role] > (_ROLE_RANK[user.role] ?? 0)) {
updates.push('role = ?'); params.push(decoded.role);
updates.push('role = ?'); params.push(decoded.role);
@@ -123,17 +123,17 @@ function resolveNetworkUser(decoded) {
@@ -123,17 +123,17 @@ function resolveNetworkUser(decoded) {
// Try matching by username (case-insensitive)
// Try matching by username (case-insensitive)
let user = db.getUserByUsername(decoded.username);
let user = db.getUserByUsername(decoded.username);
if (user) {
if (user) {
- // Auto-link this user to the openvibe.tools account
+ // Auto-link this user to the openvibe.network account
try {
try {
db.getDb().prepare(
db.getDb().prepare(
"INSERT OR IGNORE INTO linked_accounts (service, service_user_id, service_username, user_id) VALUES ('network', ?, ?, ?)"
"INSERT OR IGNORE INTO linked_accounts (service, service_user_id, service_username, user_id) VALUES ('network', ?, ?, ?)"
).run(openvibeToolsId, decoded.username, user.id);
).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 */ }
} catch { /* already linked */ }
return _syncSsoUserFields(user, decoded);
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 {
try {
const stream_key = uuidv4().replace(/-/g, '');
const stream_key = uuidv4().replace(/-/g, '');
const result = db.createUser({
const result = db.createUser({
@@ -164,7 +164,7 @@ function resolveNetworkUser(decoded) {
@@ -164,7 +164,7 @@ function resolveNetworkUser(decoded) {
"INSERT OR IGNORE INTO linked_accounts (user_id, service, service_user_id, service_username) VALUES (?, 'network', ?, ?)"
"INSERT OR IGNORE INTO linked_accounts (user_id, service, service_user_id, service_username) VALUES (?, 'network', ?, ?)"
).run(user.id, openvibeToolsId, decoded.username);
).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;
return user;
} catch (err) {
} catch (err) {
console.error(`[Auth] Failed to auto-create user for ${decoded.username}:`, err.message);
console.error(`[Auth] Failed to auto-create user for ${decoded.username}:`, err.message);
@@ -184,7 +184,7 @@ function authenticateApiToken(rawToken) {
@@ -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).
* Resolves to local user via linked_accounts (auto-creates if needed).
*/
*/
function requireAuth(req, res, next) {
function requireAuth(req, res, next) {
@@ -205,7 +205,7 @@ function requireAuth(req, res, next) {
@@ -205,7 +205,7 @@ function requireAuth(req, res, next) {
return next();
return next();
}
}
- // Fall back to openvibe.tools JWT
+ // Fall back to openvibe.network JWT
const decoded = verifyToken(token);
const decoded = verifyToken(token);
if (!decoded) {
if (!decoded) {
return res.status(401).json({ error: 'Invalid or expired token' });
return res.status(401).json({ error: 'Invalid or expired token' });
@@ -344,7 +344,7 @@ function extractWsToken(req) {
@@ -344,7 +344,7 @@ function extractWsToken(req) {
/**
/**
* Authenticate a WebSocket connection (returns user or null)
* 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) {
function authenticateWs(token) {
if (!token) return null;
if (!token) return null;
@@ -368,7 +368,7 @@ function authenticateWs(token) {
@@ -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() {
function reloadNetworkKey() {
loadNetworkPublicKey();
loadNetworkPublicKey();
@@ -1,9 +1,9 @@
@@ -1,9 +1,9 @@
/**
/**
* OpenVibe.Live — Auth Routes
* OpenVibe.Live — Auth Routes
* All authentication is via OpenVibe.Tools OAuth2 SSO.
* 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/callback — OAuth callback
* GET /api/auth/me — Current user
* GET /api/auth/me — Current user
* PUT /api/auth/profile — Update profile
* PUT /api/auth/profile — Update profile
@@ -87,12 +87,12 @@ const avatarUpload = multer({
@@ -87,12 +87,12 @@ const avatarUpload = multer({
},
},
});
});
-// ── Register (disabled — use openvibe.tools) ─────────────────────
+// ── Register (disabled — use openvibe.network) ─────────────────────
router.post('/register', (_req, res) => {
router.post('/register', (_req, res) => {
res.status(410).json({ error: 'Local registration is no longer available. Please sign in with OpenVibe.' });
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) => {
router.post('/login', (_req, res) => {
res.status(410).json({ error: 'Local login is no longer available. Please sign in with OpenVibe.' });
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) => {
@@ -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) => {
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 ───────────────────────────────────────────
// ── Get Stream Key ───────────────────────────────────────────
@@ -287,7 +287,7 @@ function getNetworkRedirectUri() {
@@ -287,7 +287,7 @@ function getNetworkRedirectUri() {
return `${config.baseUrl.toLowerCase()}/api/auth/callback`;
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) => {
router.get('/sso/login', (req, res) => {
const state = require('crypto').randomBytes(16).toString('hex');
const state = require('crypto').randomBytes(16).toString('hex');
// Store state in a short-lived cookie for CSRF protection
// Store state in a short-lived cookie for CSRF protection
@@ -363,7 +363,7 @@ router.get('/callback', async (req, res) => {
@@ -363,7 +363,7 @@ router.get('/callback', async (req, res) => {
return res.status(400).send('No user data in token response');
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);
const openvibeToolsId = String(ssoUser.id);
// Check linked_accounts first
// Check linked_accounts first
@@ -399,7 +399,7 @@ router.get('/callback', async (req, res) => {
@@ -399,7 +399,7 @@ router.get('/callback', async (req, res) => {
});
});
localUser = db.getUserById(result.lastInsertRowid);
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.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.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)) {
if (ssoUser.role && ['user', 'streamer', 'global_mod', 'admin'].includes(ssoUser.role)) {
@@ -407,19 +407,19 @@ router.get('/callback', async (req, res) => {
@@ -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);
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(
db.getDb().prepare(
"INSERT OR IGNORE INTO linked_accounts (user_id, service, service_user_id, service_username) VALUES (?, 'network', ?, ?)"
"INSERT OR IGNORE INTO linked_accounts (user_id, service, service_user_id, service_username) VALUES (?, 'network', ?, ?)"
).run(localUser.id, openvibeToolsId, ssoUser.username);
).run(localUser.id, openvibeToolsId, ssoUser.username);
localUser = db.getUserById(localUser.id); // re-fetch
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 { /* */ }
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 openvibeToolsToken = tokenData.access_token;
const openvibeRefreshToken = tokenData.refresh_token;
const openvibeRefreshToken = tokenData.refresh_token;
if (!openvibeToolsToken) {
if (!openvibeToolsToken) {
@@ -531,7 +531,7 @@ router.post('/logout', (req, res) => {
@@ -531,7 +531,7 @@ router.post('/logout', (req, res) => {
router.get('/sso/status', (req, res) => {
router.get('/sso/status', (req, res) => {
res.json({
res.json({
enabled: !!OV_CLIENT_SECRET,
enabled: !!OV_CLIENT_SECRET,
- provider: 'openvibe.tools',
+ provider: 'openvibe.network',
loginUrl: `${getNetworkBase()}/oauth/authorize`,
loginUrl: `${getNetworkBase()}/oauth/authorize`,
});
});
});
});
@@ -55,7 +55,7 @@ class ChatServer {
@@ -55,7 +55,7 @@ class ChatServer {
this.wss = null;
this.wss = null;
/** @type {Map<WebSocket, { user: object|null, anonId: string, streamId: number|null, ip: string }>} */
/** @type {Map<WebSocket, { user: object|null, anonId: string, streamId: number|null, ip: string }>} */
this.clients = new Map();
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.anonMap = new Map();
this.nextAnonId = 1;
this.nextAnonId = 1;
this._anonDbLoaded = false;
this._anonDbLoaded = false;
@@ -75,7 +75,7 @@ class ChatServer {
@@ -75,7 +75,7 @@ class ChatServer {
this.soundboardStreamLimits = new Map();
this.soundboardStreamLimits = new Map();
this._autoDeleteSweepInterval = null;
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._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 || '';
this._internalKey = process.env.INTERNAL_API_KEY || process.env.OV_INTERNAL_KEY || '';
/** @type {Map<string, Promise<number>>} IP → pending resolve promise (dedup concurrent) */
/** @type {Map<string, Promise<number>>} IP → pending resolve promise (dedup concurrent) */
@@ -126,7 +126,7 @@ class ChatServer {
@@ -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.
* Returns a Promise<number>. Caches in memory and falls back to local DB.
*/
*/
async _resolveUnifiedAnonNum(ip) {
async _resolveUnifiedAnonNum(ip) {
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
/**
/**
* OpenVibe.Live — Config
* 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.
* when available. Registry overrides take precedence over env values.
*/
*/
require('dotenv').config();
require('dotenv').config();
@@ -146,7 +146,7 @@ function buildConfig(registryValues) {
@@ -146,7 +146,7 @@ function buildConfig(registryValues) {
if (process.env.NODE_ENV === 'production' && baseUrl.includes('localhost')) {
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: 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 {
return {
@@ -240,7 +240,7 @@ function buildConfig(registryValues) {
@@ -240,7 +240,7 @@ function buildConfig(registryValues) {
openvibeBucks: {
openvibeBucks: {
// Bit-style currency: integer bucks, 100 bucks = $1.00 streamer cashout
// 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
// (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,
cashoutBucksPerUsd: 100,
minCashoutBucks: parseInt(process.env.MIN_CASHOUT_BUCKS || '500', 10), // $5.00
minCashoutBucks: parseInt(process.env.MIN_CASHOUT_BUCKS || '500', 10), // $5.00
escrowDays: parseInt(process.env.ESCROW_HOLD_DAYS || '14', 10),
escrowDays: parseInt(process.env.ESCROW_HOLD_DAYS || '14', 10),
@@ -300,12 +300,12 @@ async function refreshRegistry() {
@@ -300,12 +300,12 @@ async function refreshRegistry() {
},
},
});
});
if (!res.ok) {
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;
return config;
}
}
const body = await res.json();
const body = await res.json();
if (!body.registry || typeof body.registry !== 'object') {
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;
return config;
}
}
@@ -319,9 +319,9 @@ async function refreshRegistry() {
@@ -319,9 +319,9 @@ async function refreshRegistry() {
);
);
const updated = buildConfig(registry);
const updated = buildConfig(registry);
Object.assign(config, updated);
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) {
} 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;
return config;
}
}
@@ -207,7 +207,7 @@ function initDb() {
@@ -207,7 +207,7 @@ function initDb() {
database.exec(`CREATE INDEX IF NOT EXISTS idx_vkeys_status ON verification_keys(status)`);
database.exec(`CREATE INDEX IF NOT EXISTS idx_vkeys_status ON verification_keys(status)`);
} catch (e) { console.warn('[DB] verification_keys migration:', e.message); }
} 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 {
try {
database.exec(`CREATE TABLE IF NOT EXISTS linked_accounts (
database.exec(`CREATE TABLE IF NOT EXISTS linked_accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -812,7 +812,7 @@ function initDb() {
@@ -812,7 +812,7 @@ function initDb() {
const seedGoogle = database.prepare("INSERT OR IGNORE INTO site_settings (key, value, description, type) VALUES (?, ?, ?, ?)");
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);
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.
// Master switch is OFF by default so nothing goes live until an admin enables it.
const paymentSeeds = [
const paymentSeeds = [
['payments_enabled', 'false', 'Master switch: enable real-money purchases & subscriptions', 'boolean'],
['payments_enabled', 'false', 'Master switch: enable real-money purchases & subscriptions', 'boolean'],
@@ -870,7 +870,7 @@ function initDb() {
@@ -870,7 +870,7 @@ function initDb() {
}
}
} catch (e) { console.warn('[DB] Vibes bit migration:', e.message); }
} 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.
// OFF by default so no API calls (or cost) happen until an admin enables it.
const aiSeeds = [
const aiSeeds = [
['ai_enabled', 'false', 'Master switch: enable AI analysis (pastes + stream memories)', 'boolean'],
['ai_enabled', 'false', 'Master switch: enable AI analysis (pastes + stream memories)', 'boolean'],
@@ -126,16 +126,16 @@ function getAllowedOrigins() {
@@ -126,16 +126,16 @@ function getAllowedOrigins() {
// Warn loudly when BASE_URL wasn't set and we're in production
// Warn loudly when BASE_URL wasn't set and we're in production
if (process.env.NODE_ENV === 'production' && baseOrigin.includes('localhost')) {
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: 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);
const openvibeToolsOrigin = normalizeOrigin(config.openvibeToolsUrl || process.env.OV_NETWORK_URL);
if (openvibeToolsOrigin) {
if (openvibeToolsOrigin) {
allowed.add(openvibeToolsOrigin);
allowed.add(openvibeToolsOrigin);
} else {
} 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');
allowed.add('https://openvibe.network');
}
}
@@ -212,7 +212,7 @@ app.use(helmet({
@@ -212,7 +212,7 @@ app.use(helmet({
crossOriginEmbedderPolicy: false,
crossOriginEmbedderPolicy: false,
}));
}));
// The cross-site go-live widget + its SSE feed must be loadable from other OpenVibe
// 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) => {
app.use((req, res, next) => {
if (req.path === '/live-notify.js' || req.path === '/api/live-events') {
if (req.path === '/live-notify.js' || req.path === '/api/live-events') {
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
@@ -923,7 +923,7 @@ async function start() {
@@ -923,7 +923,7 @@ async function start() {
console.log('[Server] Ready. Good vibes only. ▶');
console.log('[Server] Ready. Good vibes only. ▶');
console.log('');
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/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/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); }
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() {
@@ -1152,8 +1152,8 @@ async function start() {
}, 60000);
}, 60000);
if (typeof maintenanceInterval.unref === 'function') maintenanceInterval.unref();
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.
// unreachable), subsequent refreshes will fix CORS, issuer, and other URL config.
const registryRefreshInterval = setInterval(async () => {
const registryRefreshInterval = setInterval(async () => {
try {
try {
@@ -26,10 +26,10 @@ router.post('/url-registry/refresh', async (req, res) => {
@@ -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
// 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
// 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) => {
router.post('/user-role', (req, res) => {
try {
try {
const { username, openvibenetwork_id, role } = req.body || {};
const { username, openvibenetwork_id, role } = req.body || {};
@@ -3,7 +3,7 @@
@@ -3,7 +3,7 @@
*
*
* Real-money purchase of Vibes + channel subscriptions across PayPal,
* Real-money purchase of Vibes + channel subscriptions across PayPal,
* Stripe, CCBill and crypto (NOWPayments). All provider credentials live in
* 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
* `payments_enabled` gates everything (default OFF) so nothing is live until an
* admin turns it on.
* admin turns it on.
*
*
@@ -27,7 +27,7 @@ function isEnabled() { return b('payments_enabled'); }
@@ -27,7 +27,7 @@ function isEnabled() { return b('payments_enabled'); }
/** USD (dollars) → whole Vibes at the VALUE rate (100 bucks = $1). Used for
/** 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
* 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) {
function bucksForUsd(usd) {
const rate = n('bucks_per_usd', 100);
const rate = n('bucks_per_usd', 100);
return Math.max(0, Math.round(usd * rate));
return Math.max(0, Math.round(usd * rate));
@@ -112,7 +112,7 @@ router.post('/donate', requireAuth, (req, res) => {
@@ -112,7 +112,7 @@ router.post('/donate', requireAuth, (req, res) => {
db.saveChatMessage({
db.saveChatMessage({
stream_id: stream_id || null, channel_user_id: streamer_id, user_id: null,
stream_id: stream_id || null, channel_user_id: streamer_id, user_id: null,
username: 'Donation Goal',
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',
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 },
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 },
});
});
@@ -81,9 +81,9 @@ const { notifyDiscordGoLive } = require('../integrations/discord-webhook');
@@ -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 || '';
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) {
function notifyFollowersGoLive(streamer, stream) {
// Try unified event endpoint first (handles Discord bot + push)
// Try unified event endpoint first (handles Discord bot + push)
@@ -119,7 +119,7 @@ function notifyFollowersGoLive(streamer, stream) {
@@ -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) {
function _fallbackNotify(streamer, stream) {
notifyDiscordGoLive(streamer, stream);
notifyDiscordGoLive(streamer, stream);
@@ -30,7 +30,7 @@ function pushNotification(payload) {
@@ -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.
// appears under their Linked Services. Fire-and-forget + deduped per process.
const _linkedReported = new Set();
const _linkedReported = new Set();
function reportLinkedAccount(user) {
function reportLinkedAccount(user) {