Imported from OpenVibers/OpenVibe.Live (
AGENTS.md). Install upstream withnpx skills add OpenVibers/OpenVibe.Live. Copyright stays with the author.
AGENTS.md — OpenVibe.Live
Project Overview
Self-hosted live streaming platform — Node.js/Express monolith, vanilla JS SPA frontend, SQLite (better-sqlite3). Part of the OpenVibe network with SSO via openvibe.network. See README.md for features and docs/architecture.md for system design.
Network context: SSO/OAuth2 + the OpenCoins wallet come from OpenVibe.Network (server/monetization/wallet-client.js). The media subsystem (VODs/clips/pastes/thumbnails/files) lives in OpenVibe.Media — Live talks to it via server/media-client.js, the thin proxy routers in server/media-proxy/, and the Media-backed recorder server/streaming/recorder.js. Media completion events arrive over OpenVibe.Events at POST /internal/media-events (MEDIA_EVENTS_AUTHORITY; the older POST /internal/media-webhook stays during the transition). The legacy local vods/clips/pastes/paste_likes/paste_comments tables are FROZEN: never read or write them (test/frozen-tables.test.js enforces it; ask Media or Community through server/media-proxy/lookups.js, media-client.js, pastes-client.js); VOD/clip comments are OpenVibe.Community threads (server/comments-client.js) and the local comments table is READ-ONLY too; Live-owned AI/transcript state for Media-hosted content lives in vod_ai_state/clip_ai_state. See ../CONTRACTS.md for the binding inter-service contracts. Money: BILLING_AUTHORITY (read only in server/monetization/money-authority.js) is live by default; billing makes OpenVibe.Billing the ledger (billing-client.js, billing-actions.js) and Live's openvibe_bucks_*/transactions/payment_orders/subscriptions read-only (a database-level tripwire throws). The owner-only money_writes_frozen flag (/api/admin/money) refuses money actions in both modes.
Commands
npm run dev # Start dev server (NODE_ENV=development)
npm start # Start production server (node server/index.js)
npm run init-db # Initialize database from schema.sql
npm run seed # Seed sample data
node --check <file.js> # Syntax check (no linter configured)
npm test # All unit/security/migration/deploy tests + size budgets (test/run.js)
node test/<file>.test.js # One test
BASE=http://127.0.0.1:3000 npm run test:browser # Browser smoke (running server + Chrome)
No build step. Frontend is plain JS served directly — no bundler, no transpiler. Asset URLs are content-hashed at serve time (server/web/assets.js) — never add ?v= by hand.
Deploy: production runs the release layout (/opt/openvibe.live/current → releases/<time>-<sha>; deploy with cd /opt/openvibe.live/current && sudo deploy/scripts/deploy.sh), env /etc/openvibe/live.env, unit openvibe-live.service. Static-only changes deploy without a restart. See docs/deploy.md.
Architecture at a Glance
- Entry: server/index.js — Express app, middleware, route mounting, WS upgrade handler, sub-service init
- Config: server/config.js reads
.env(.env.example) - Database: server/db/database.js — all queries, schema in server/db/schema.sql
- Auth: server/auth/auth.js — RS256 JWT from openvibe.tools SSO +
hbt_API tokens - Permissions: server/auth/permissions.js — role hierarchy:
user < streamer < global_mod < admin - Frontend shell: public/index.html — navbar, home page and empty
<section id="page-*">shells; routing viahistory.pushStatein public/js/app.js - Route loading: public/features.json maps routes → features (fragment in
public/fragments/, CSS inpublic/css/features/, scripts, deps, stubs); public/js/ov-loader.js loads them. New page code goes in a feature, not inindex.html/coreapp.js. See docs/architecture.md.
Each feature lives in its own server/<feature>/ directory with routes.js + service files. Frontend: one JS file per feature in public/js/, registered in public/features.json.
Conventions
- CommonJS (
require/module.exports) everywhere. No ES modules except dynamicimport()for mediasoup-client. - Style: 4-space indent, single quotes, semicolons. No linter/formatter configured.
- Naming:
camelCasefor JS,snake_casefor SQLite columns/tables. - DB access: Direct
better-sqlite3calls indatabase.js(e.g.,db.getUserById(),db.run(),db.get(),db.all()). - Auth middleware:
requireAuthfromauth.js. Permission checks viapermissions.js. - DB migrations: Idempotent
CREATE … IF NOT EXISTS/ADD COLUMNmay stay inline; anything that transforms data goes in server/db/migrations.js (ledger, transaction,adopt,DEFER). - Public responses: serialize through server/web/serializers.js — never return raw
managed_streams/usersrows. - TURN: ICE lists come from server/net/turn.js; set
TURN_AUTH_SECRET(coturnuse-auth-secret) for short-lived credentials. - Outbound fetches of user-chosen URLs: server/net/egress.js only. Background loops:
server/utils/jobs.js. - Files and restore drills: every file location comes from server/paths.js (
DATA_DIR,DB_PATH), never a literal./data. Anything started at boot or at module load (timers, listeners, sockets, jobs, outbound calls) must stay off underLIVE_DRILL(server/drill.js);test/drill-mode.test.jsfails on any new one. - WebSocket servers: Each has
init(server)andhandleUpgrade(req, socket, head)methods. - Frontend globals:
currentUser,api(),navigate(),handleLinkClick(). Cross-component sync viaCustomEvent(e.g.,openvibe-auth-changed). - ChatServer: Singleton —
chat-server.jsexportsnew ChatServer(), not the class.
Key Pitfalls
- No build step: Changes to
public/take effect on deploy without a restart; caching follows content hashes.npm testfails if the home page's size budgets grow (scripts/perf/check-budgets.js). - Inline handlers on lazy features: a function called from
onclick=in markup that exists before its feature loads must be listed in that feature'sstubs. - New page routes: the SPA fallback answers 404 for any path server/web/page-status.js does not know. A new top-level route goes in both
routeFromURL()and that file's page list. - innerHTML usage: Frontend has heavy
innerHTML— prefer DOM node creation for new code to avoid XSS. - WebSocket auth lifecycle: WS connections can start anonymous and upgrade via
joinmessage. On account switch, the socket must be rebuilt (not just re-joined) — seeopenvibe-auth-changedhandling inchat.js. - openvibe-shared: Pinned release of OpenVibers/OpenVibe.Shared (
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/vX.Y.Z"), served at/shared/*fromnode_modules. Change it there and bump the tag; never editnode_modules. - DM delivery: Server verifies
dm.isParticipant()before delivering — always maintain this check. - Schema:
ensureTables()functions create tables on first use. Some modules (DMs, arena, etc.) have their ownensureTables().
WebSocket Endpoints
/ws/chat, /ws/broadcast, /ws/control, /ws/call, /ws/robotstreamer-publish — all upgraded via handler in server/index.js with origin checks and IP bans.
Testing
Standalone Node scripts in test/ using assert, run together by npm test. They create temp SQLite databases. Browser smoke: test/browser/smoke.js (routes × widths, console errors, overflow, duplicate scripts, resource growth). Always node --check modified files before committing.
Documentation
Every file below is also served on the site at /docs/<name> (rendered by server/docs/routes.js, GitHub-compatible heading anchors) — link to https://openvibe.live/docs/whip#… rather than to GitHub when pointing users at a doc.
- docs/architecture.md — System design, data flows, module map
- docs/broadcasting.md — Streaming protocols (WebRTC/RTMP/JSMPEG/WHIP)
- docs/whip.md — WHIP ingest API reference (auth forms, CORS, browser-only publishing, error codes)
- docs/arena.md — Arena tab (streamer vs streamer): ratings, AI personas, gated portrait generation, battles, votes
- docs/chat-system.md — Chat features and moderation
- docs/api-tokens.md — Bot/integration token system
- docs/vods-and-clips.md — VOD/clip pipeline (pre-split; storage/cutting now in OpenVibe.Media)
- docs/dashboard.md — Streamer dashboard
- docs/onboarding.md — New user flow
- docs/deploy.md — Deploy kinds, release layout, caching, nginx
- docs/performance-audit.md — Measurements and tools
- SETUP.md — Full deployment guide
- SECURITY_AUDIT.md — Security audit findings
- hardware/README.md — Raspberry Pi integration (moved to OpenVibe.Extensions
hardware/; this is a redirect)