Skip to content
-
openvibe-shared 1.23.0 -> 1.23.1: release-watch drops an in-place region fetched for the previous account when openvibe-auth-changed fires and fetches it again with the new session (D46 scenario 9, which Live's in-place account switch needs), and an online tab reconnects the release stream at once even while backing off (scenario 14). npm test 142/142.OpenVibers · 27 Sept 2026
0481e9ce
-
N-1 fixtures re-recorded from efab014, the release now in production (npm run n-1:record).OpenVibers · 27 Sept 2026
29d68089
-
Live declares its release components, so a stylesheet-only release is swapped into open tabs in place (D46 scenario 1; Host docs/release-acceptance.md gate 1e, which was open). server/web/release-components.js has three components. styles: every stylesheet under public/css, kind style, applied in place with no reload, so a broadcaster's desk or a viewer's player never reloads for a colour. shell: scripts, pages, fragments, the feature registry, the service worker, OBS overlays, server/web, server/seo and the lockfile (the /shared files come from the pin), kind script, prompted. server: the rest, nothing to do in tabs. Before, Live declared none, so every release prompted a reload. The release root is the current link (OV_APP_ROOT) and the manifest is rebuilt when files change (recheckMs, lazily, no timer). The release id is read from where current points at every rebuild, so a static-only switch, which does not restart the process, reports the release it is. test/release-components.test.js: a stylesheet changes styles only, a script the shell, server code server only, page-stamping server code the shell, and the real tree lists all its stylesheets. npm test 142/142.OpenVibers · 27 Sept 2026
efab0149
-
Live follows account merges (roadmap WS-B task 5, ADR-029; openvibe-contracts 0.68.0 -> 0.70.0). network.subject.merged arrives at POST /internal/network-events (the --network subscription now asks for it) and server/auth/subject-merge.js applies it once per merge_id (subject_merges). With a Live user for each account, the folded-in user's follows (as follower and as the channel), channel points (as viewer and as the channel; a pair both hold is summed, each move logged out and in in channel_points_log with reason account_merge) and streams move to the survivor's user in one transaction, a follow it already has or following oneself dropped and counted, and the folded-in user is marked merged_into. With only the folded-in account on Live, that user is relinked to the survivor (subject and Network user id, resolved from Network). The handler answers after the merge applied, so a failure is redelivered. test/subject-merge.test.js: moves and clashes, summed points and the balanced log, streams, the relink, redelivery, nothing-to-do and ignored cases. npm test 141/141.OpenVibers · 27 Sept 2026
4b7fa3f0
-
N-1 fixtures re-recorded from f0edbc0, the release now in production (npm run n-1:record; the first Live release deployed by ovhost's release-layout strategy).OpenVibers · 27 Sept 2026
e663abb5
-
deploy/scripts/deploy.sh hands over to ovhost deploy live (OpenVibe.Host strategy release-layout; roadmap WS-N task 11). The old script is kept, unchanged, as deploy/scripts/deploy-legacy.sh. The wrapper maps --wait-idle, --restart, --rollback (ovhost rollback live), DRY_RUN=1 (ovhost plan live) and READY_TIMEOUT, and refuses --force, which meant 'discard local tracked changes' here but drops live streams in ovhost. It asks ovhost capabilities live first and runs deploy-legacy.sh with the same arguments, saying why, when ovhost is missing, too old (no capabilities, deploy-api < 1) or the host inventory does not deploy live with release-layout (OVHOST_LEGACY=1 forces it), so a deploy never breaks while the host is cut over. test/deploy-wrapper.test.js covers the mapping and the fallback; test/deploy-sim.test.js now runs deploy-legacy.sh; test/systemd-units.test.js keeps the listener check and the changed-socket restart assertions on deploy-legacy.sh and checks the wrapper only hands over to release-layout, whose socket rule OpenVibe.Host tests. docs/deploy.md, deploy/README.md, README.md.OpenVibers · 27 Sept 2026
f0edbc03
-
Live's feature loader runs on the network's web runtime (roadmap WS-P task 6; openvibe-shared 1.22.0 -> 1.23.0). public/js/ov-loader.js was extracted into openvibe-shared/web-runtime.js and is now a 50-line wrapper: it reads the #ov-assets registry and hashes, keeps Live's stylesheet slot (split-out rules above the ov-css-anchor, as server/web/assets.js isEarlyCss), the stub-failure toast and the route-error icon, and exposes the same window.ov, ovLoadRoute and ovPrefetchRoutes. index.html loads /shared/web-runtime.js (served and hashed like the other shared files) just before it. What Live gains: a feature whose script fails rolls back (its new stylesheets leave, the retry fetches only what is missing), route scopes refuse registrations that arrive after their route ended, scope.fetch aborts with the route, a prefetch budget, and ov.diagnostics()/ov.leaks(). Home page eager JS is 259.7 of 260 KB brotli (the runtime used the headroom; no budget raised). npm test 139/139; test/browser/smoke.js against a local drill instance passes (every route at 4 widths, no script twice, no navigation errors, no growth over laps); ov.leaks() is empty after six SPA navigations.OpenVibers · 27 Sept 2026
d4c15e17
-
N-1 fixtures re-recorded from 2e6f546, the release now in production (npm run n-1:record).OpenVibers · 27 Sept 2026
8525ff1b
-
The soundboard's audio download cannot be steered inward (roadmap WS-R task 5: SSRF; low; the same fix as OpenVibe.Chat's copy). The 101soundboards audio URL comes from that API's answer; its check allowed only 101soundboards.com but judged one DNS answer with its own list, which missed ::ffff:127.0.0.1 and the other IPv6 spellings of internal addresses and CGNAT, and the download resolved the name again. Every answer must now be public by openvibe-shared/egress's rule, and the download connects through its safeLookup. test/security-ssrf.test.js covers it (a mapped-IPv6, CGNAT and mixed answer refused, rebinding refused at connect time); the in-process security suites keep their DATA_DIR in a temp directory, so nothing lands in the checkout's data/.OpenVibers · 26 Sept 2026
2e6f5464
-
IDOR and DM privacy suites (roadmap WS-R task 5: IDOR on objects, private room bypass). test/security-idor.test.js tries every write route that takes an object id with another streamer's id: VODs and clips through Live's Media proxy (edit, delete, publish, title, visibility, recut, trim, bulk actions with a list of ids, old-item cleanup, recording chunks and finalize into someone else's stream, a live thumbnail), streams (edit, end, delete, heartbeat, call settings), slots (edit, delete, key regeneration, profile, vibe-coding settings and events; account-key regeneration touches only the caller's key), restream destinations (edit, delete, start, stop, and lists), API tokens, channel emotes, ONVIF cameras and presets. Each refusal leaves the row as it was (or Media uncalled) and carries no key or title; positive controls show each route works for its owner; anonymous gets 401. All held.OpenVibers · 26 Sept 2026
8539e3fb
-
A streamer's own AI endpoint goes out through the egress guard (roadmap WS-R task 5: SSRF). The AI viewers' "bring your own" base URL is typed by the streamer, but llm.js POSTed to it with a plain fetch: any signed-in user could point POST /api/ai-viewers/byo/test at 127.0.0.1 (Live itself, Tools, Network's internal routes), a private address or cloud metadata, and the "test connection" answer carried the first 200 characters of whatever answered (a saved base URL did the same every time the bots ran). A provider that is not the site's own (p.shared false) now goes through egress.postJson, new in server/net/egress.js: the address is checked where the connection is made, redirects are not followed. The site's provider (ai_base_url, owner-set) may still be internal.OpenVibers · 26 Sept 2026
aeb3e688
-
Security crawl for stream keys and secrets (roadmap WS-R task 5: stream-key exposure, internal-secret leak). test/security-crawl.js boots the real server/index.js as a restore-drill instance (reads only, no outbound connection) on a seeded temp database, lists every GET route Express knows after boot (so a route added later is crawled without anyone listing it) and requests each one, with seeded ids, a query string and nonsense ids, plus the SPA/SSR pages and the probes, as several signed-in people (real RS256 session tokens from a test Network key).OpenVibers · 26 Sept 2026
1734f3e9
-
Creator analytics through Network (roadmap WS-E task 6; Contracts v0.68.0, pinned from 0.56.0). live.stream.ended carries the stream's totals (stats: peak and average viewers, unique chatters, messages, watch minutes; counts only), computed in the transaction that ends the row. With ANALYTICS_SOURCE=network, /api/analytics/channel/:username and its dashboard read Network's creator analytics with Live's service token (network.analytics.creator.read) and merge Live's own per-stream extras (coins, new followers, clips). Unset, or with no subject or Network down, they use Live's tables as before. test/network-analytics.test.jsOpenVibers · 26 Sept 2026
83ad224f
-
Follows, ADR-030 step 4 in Live. With FOLLOWS_AUTHORITY=network, both follow buttons record the change on Network first (PUT/DELETE /internal/follows/channel/<subject>, Live's service token, network.follows.write) and write Live's row only when Network took it; when Network cannot answer the button fails with 503 instead of drifting. A side without a Network subject stays Live-only. network.follow.created/.deleted (added to the --network subscription) keep Live's follows table as a projection, applied in revision order (follow_projection_revisions). Unset: the buttons write Live only, as before. test/network-follows.test.jsOpenVibers · 26 Sept 2026
813f4ad8
-
A screen-mode slot goes live from a phone with the camera (roadmap D20 parity: mobile broadcasting). Phone browsers have no getDisplayMedia, so a slot set to screen sharing could not go live from a phone at all: capture threw 'getDisplayMedia is not a function' (twice, via the minimal-constraints retry). startMediaCapture now broadcasts the camera there and says so, without saving over the slot's mode (browser_mode turns screen sharing back on from a computer); the hosted WHIP publisher greys out its Screen option where the browser cannot share. test/mobile-broadcast.test.js checks what npm test can without a browser: the inline muted preview and viewport, the phone-width layout (stacked, scrollable clear of the home indicator, two-column controls, dvh-capped preview, chat as a bottom sheet, taller portrait preview), the chat button shown only while live on a phone and opening the sheet (the page's own functions), the capture fallback chain, the front/rear camera switcher, the wake lock, no screen-share button without getDisplayMedia, and the WHIP publisher on a phone.OpenVibers · 26 Sept 2026
6212df76
-
Chat-log views verified by test (roadmap D20 parity: chat-log UI). test/chat-log-ui.test.js runs the dashboard's own Chat Logs functions (loadDashChatLogs, dashPurgeChatRange) on a fake DOM against the real chat routes: a streamer sees their own stream's log, escaped and newest first, narrowed by search, user and local-time range (exactly the minute asked for, which needed efe74a4's range fix), in pages of 50; a range purge goes preview, confirm, purge, reload. The server keeps a streamer to their own streams (other channels and full exports refused; their older streams by id), staff audit purged lines. The mod view (the user card's View Chat Logs) reads user, anonymous and relay chatter history and search: staff see anyone's, everyone else only their own.OpenVibers · 26 Sept 2026
78df1250
-
A second RTMP slot's encoder no longer takes over the first slot's stream (roadmap D20 parity: per-stream control profiles). An RTMP publish attached to any live RTMP stream of the user that was waiting for an encoder, so with two slots on RTMP the second slot's encoder took over the first slot's stream: it rebound it to the second slot's control profile, fed it from two encoders, ended it when that encoder left, and the second slot never got a stream. A slot key now picks up only its own slot's waiting stream; the account key one that no other encoder feeds. test/slot-control-profiles.test.js runs the real publish handlers on a fake node-media-server: Go Live then encoder keeps the slot's profile, a second slot gets its own stream and buttons, a slot without a profile gets the channel default, one encoder leaving ends only its stream, each stream serves its own controls, a profile edit reaches only the streams bound to it, rebinding is limited to the owner's profiles; WHIP, the Go Live page and the OpenRe mirror apply the slot's profile first.OpenVibers · 26 Sept 2026
d565a2e2
-
Opt-in low-bitrate alert, and the disconnect alert for encoder streams (roadmap D20 parity: audible disconnect and low-bitrate alerts). The broadcaster's opt-in disconnect beep existed for browser broadcasts only: the encoder settings' 'Disconnect Audio' toggle was never consulted because only WebRTC connection states raised the alert, and the banner went inside the hidden browser section. The RTMP feed stopping after it was received now raises it (above the feed status; cleared when the feed returns or the stream ends). The low-bitrate alert was missing: with 'Low Bitrate Alert' on (Audio settings, off by default), a connected browser broadcast whose upload stays under 30% of its target for three stats samples (~18 s) gets one warning and a lower beep per episode. Both opt-ins are read from this browser's storage when the page loads and restored into every toggle (the encoder toggle was never restored). test/broadcast-alerts.test.js runs the page's own alert functions on a fake DOM: browser drops, RTMP feed loss/return/end, the low-bitrate threshold, episode and opt-in rules, and the toggles' wiring.OpenVibers · 26 Sept 2026
5608a6c1
-
The RTMP broadcaster's live preview shows again; live thumbnail cadence pinned (roadmap D20 parity: thumbnail cadence). The preview asked for /thumbnails/stream-<id>-live.jpg, which nothing served since live thumbnails became /api/thumbnails/stream-<id>-<ts>.jpg (a new name per capture), and its <img> was loading="lazy" inside a box that stays hidden until the image loads, so it could not load anyway. /api/thumbnails/stream-<id>-live.jpg now answers with that stream's current live thumbnail (no-cache; a Media fallback frame is a 302; 404 when there is none so the preview stays hidden) and the image is eager. test/live-thumb-cadence.test.js pins the cadence end to end: captures replaced after 2 minutes and not before, writes under 15 s apart dropped, a new file name per capture and the old file removed, files deleted after an hour; the 60 s maintenance loop's 2-minute grabs for RTMP, JSMPEG and WebRTC/WHIP; the browser's 115 s post from a visible tab on its 30 s heartbeat; the home grid's once-a-minute re-read and crossfade; the preview's 10 s poll of the served name.OpenVibers · 26 Sept 2026
cb70d300
-
The broadcaster's Viewer preview opens the slot on air (roadmap D20 parity: viewer-style preview). openViewerPreview() put streamData.slug || streamId in the /@user/<slot> position, but streamData is the stream row: it has no slug, so the live stream's own id went in, and the channel page resolves that segment as a SLOT id, so the popup showed another slot (or the channel default) instead of what viewers of this stream see. It now uses the row's managed_stream_slug, else managed_stream_id, and the plain channel page for a stream without a slot. test/broadcast-viewer-preview.test.js runs the page's own openViewerPreview() and channelPath() against broadcast states (slug, slot without slug, no slot, nothing live, guest) and shows on a temp database that the old segment resolved to the other slot and the new one to the slot on air.OpenVibers · 26 Sept 2026
86693f94
-
Purges and deletes stay consistent with replay (roadmap D20 parity: replay purge consistency). (1) The dashboard's chat purge, its preview and the log filter send ISO instants ('...T...Z') and chat_messages keeps SQLite's 'YYYY-MM-DD HH:MM:SS'; compared as TEXT ('T' sorts after ' ') a range matched nothing on its first day and all of its last, so a purge removed lines outside the chosen range, kept the ones inside it, and VOD chat replay kept showing them. Live's range queries (purge, preview, logs, replay) now use datetime(?), as OpenVibe.Chat does since e26e063: this is the CHAT_AUTHORITY=live path and the mirror Live keeps under chat. (2) Deleting a VOD or clip only hid its comment thread: Live kept its AI state (the backfill takes vod_ai_state newest-first, so deleted VODs sat at the head of the overview queue for good), its unique views, and after an admin or older-than delete its Search document until the daily refresh. Every delete path now ends in server/media-proxy/purge.js once Media confirmed it: thread hidden, vod_ai_state/clip_ai_state and content_views rows dropped (db.forgetMediaItem), Search document re-read. docs/chat-system.md had the purge body as fromTime/toTime; it is from/to. test/replay-purge.test.js: ISO ranges agree across preview, purge, logs and replay (same day and across midnight), purges are soft and owner-scoped, and each delete path drops exactly the deleted items' rows.OpenVibers · 26 Sept 2026
efe74a40
-
Live VODs extend toward the live edge again (roadmap D20 parity: VOD tail, finalisation, seek). /api/vods/:id/live-info said seekable: false for every recording, because it trusted only a 'seekable' field OpenVibe.Media's rows do not have; the VOD page refreshes its source (keeping the viewer's position) only when that is true, so a viewer stayed at the length the recording had when they opened it. A recording is seekable once Media has written bytes (its first periodic pass, ~30 s in, stores file_size and the indexed copy /v/<id> serves). test/vod-tail-finalize.test.js pins Live's side with Media and the SFU stubbed: RTMP, WebRTC/WHIP (PlainRTP to Media's ports), browser-chunk and clips-only recordings start and finalise with the right Media calls, once; the live-VOD, live-info and file-redirect endpoints; vod.ready clearing the recorder; and the VOD page's tail, finalise, jump-to-live and ?t= seek wiring.OpenVibers · 26 Sept 2026
688f178d
-
N-1 fixtures re-recorded from f9599c6, the release now in production (npm run n-1:record).OpenVibers · 26 Sept 2026
b15f0f70
-
Channel pages show the owner's game progress (roadmap WS-M task 2, PF7): GET /api/streams/channel/:username/game reads the public fields of their games.progress.summary user module (level, achievements, playtime) from Network's public module endpoint. That needs no token or grant; it is what Network shows anyone. Answers and misses are cached 10 minutes; Network trouble is not cached; 204 when there is none. A small 'Scraplandia · Lv 12 · …' badge next to the followers, live and offline, built with DOM nodes. test/game-summary.test.jsOpenVibers · 26 Sept 2026
f9599c63
-
Live's bell is realtime too: the shell initialises OpenVibeNotifications itself (before the shared navbar, which then leaves it alone), so the navbar's notificationsRealtime (13525eb) never reached it. The shell's init now passes realtime: true. Found with a signed-in browser check: Blog's bell opened a stream, Live's stayed on polling.OpenVibers · 26 Sept 2026
c458b2e7
-
N-1 fixtures re-recorded from 4479b66, the release now in production (npm run n-1:record).OpenVibers · 26 Sept 2026
2d848559
-
Unresolved lineage references for operators (roadmap D20, remaining item 2). Every unresolved answer of the canonical resolver is counted in lineage_unresolved by its inputs (canonical 'clip_id=7 slug=x') and reason, with detail, last caller, count and first/last seen, and kept 30 days: to another service through /internal/lineage/resolve, and Live's own clip-owner check. Reasons: conflict, source_unavailable, not_found, display_name_only; no_input is not kept. Staff read them at GET /api/admin/lineage/unresolved (?reason=, counts per reason). Recording never changes an answer. test/lineage-resolver.test.jsOpenVibers · 26 Sept 2026
4479b667
-
N-1 fixtures re-recorded from 13525eb, the release now in production (npm run n-1:record).OpenVibers · 26 Sept 2026
257bb254
-
The notification bell hears new notifications as they happen (notificationsRealtime; Shared 1.22.0, roadmap WS-E task 3): Network's network.notification.created over OpenVibe.Events' realtime stream, with polling as the fallback. Only a signed-in bell with a Network token opens a stream.OpenVibers · 26 Sept 2026
13525ebd
-
N-1 fixtures re-recorded from 5caa46d, the release now in production (npm run n-1:record).OpenVibers · 26 Sept 2026
c85121d1
More commits