m1/org/openalice-liveci failure

openalice-live

**Streaming product on the OpenAlice platform** (paused; structure consolidated 2026-07-06, builds get fixed AT WAKE — deps then switch to the registries: kellnr crates.blal.pro + Verdaccio npm.blal.pro, per the embed pattern).

Features
322
Status
·unknown
Last activity
11d ago
Branch
live-spatial-splat-scene

Architecture rating

F10d ago
docsD

Manifest present+valid with 322 features, AGENTS.md exists, docs/ dirs in 4 sub-projects; but 1513 missing docstrings, README states 'paused', and the massive undeclared-env list (80+ vars) shows runtime config is undocumented.

observabilityD

OTEL_EXPORTER_OTLP_ENDPOINT + OTEL_SERVICE_NAME + dedicated OA_OBSERVABILITY_PORT show intent; GET /api/health endpoint declared but returns 404; structured logging unverifiable; tracing wired but non-functional given 0% uptime.

data layerD

Only 2 migration files (0001_chat_messages, 0001_broadcasts); manifest owns zero tables; OA_TENANT_ID env var suggests tenant awareness but no evidence of RLS or FK constraints; DATABASE_URL + BROADCASTS_DB_URL + CHAT_BRIDGE_DB_URL split across services with no schema governance.

securityD

SHARED_JWT_SECRET + OA_INTERNAL_SECRET + OA_TWITCH_OAUTH among 80+ undeclared env vars — secrets exist but zero manifest governance; TENANT_RATE_LIMIT_PER_MINUTE hints at rate-limiting but no evidence of SSRF guards, CSP headers, or injection defenses; auth endpoints referenced (OA_AUTH_URL) but unverifiable with 0% uptime.

consistencyF

5 circular dependency cycles (registry ↔ *App), 133 god modules, 52 duplicate signatures, 42 dynamic imports, 733 format issues; manifest valid and AGENTS.md present but internal code patterns are deeply inconsistent across the 6 sub-projects.

testingF

vitest.config.ts in live-hub + live-web and e2e/ directory exist but CI is red (failure); qa-audit scripts present yet contain dead imports; no test count or coverage metric available; critical paths (scene WS, PTY sessions, broadcast lifecycle) unverifiable.

reliabilityF

140 bare/broad excepts, max cyclomatic complexity 124, 37 deep-nesting sites; CI conclusion=failure, health probe returns 404, uptime_24h=0%; watchdog env var exists (WATCHDOG_INTERVAL_SECS) but service is down — no evidence of graceful shutdown, retries, or idempotency.

source · auto-grader-2026-07-08

Ecosystem manifest

.atlas-deps.yml

Next.js streamer studio + OBS Browser Source page (live.blal.pro) — VRM avatar renderer, 9 scene modes (incl. vibecode live-coding layout), agent-driven scene WS, BGM/SFX engine, chat overlay.

Runtime deps
  • openalice-authshared-jwt
    via SHARED_JWT_SECRET
    JWTs issued by openalice-tenants are signed with the same shared secret so /auth/me + every openalice service accepts them without a separate SSO round-trip.
  • via BROWSE_INTERNAL_URL
    Server-side proxy route (/api/browse-session) mints openalice-browse sessions for the in-scene BrowserApp OS window; internal secret stays server-side.
  • via NEXT_PUBLIC_CDN_URL
    Serves BGM preset MP3s (cdn.<domain>/audio/bgm/<id>.mp3) and SFX sound-pack assets (cdn.<domain>/audio/sfx/<pack>/<cue>.mp3) referenced by the BgmPlayer and SfxPlayer.
  • via NEXT_PUBLIC_LIVE_CONTROL_URL
    Scene-control WebSocket at /realtime/scene/<broadcast_id>/control — receives ChangeScene ToolCalls + SceneEvent frames from the agent mid-stream without restarting broadcaster Chromium.
  • via NEXT_PUBLIC_LIVE_CONTROL_URL
    HTTP routes /live/start, /live/stop, /live/status, /live/chat-out called by Studio Hub + Studio Chat with the user's bearer token.
  • via NEXT_PUBLIC_DEFAULT_DOMAIN
    Embeds the VRM avatar renderer as an iframe (persona.blal.pro/<user>) in every VRM-bearing scene mode; forwards ?avatar=, ?anim=, ?token= from the parent URL.
  • via PTY_INTERNAL_URL
    Server-side proxy route (/api/pty-session) mints openalice-pty sessions for the in-scene TerminalApp OS window; internal secret stays server-side, returns a fully-qualified wss:// URL for the browser.
  • via NEXT_PUBLIC_RADIO_URL
    RadioPlayer subscribes to wss://radio.<domain>/v1/radio/channels/<slug>/subscribe; each now_playing frame drives the BgmPlayer cross-fade so the live page tracks the current radio track without restarting BGM.
  • via NEXT_PUBLIC_RT_URL
    Realtime presence / event bus consumed via @openalicelabs/realtime; used by the scene page control-WS helper (useRealtime) for live broadcast events.
  • via NEXT_PUBLIC_TENANTS_URL
    Tenant auth flow (login, signup, password-reset, email-verify, consent, GDPR export, account-deletion, paired-agent grants) consumed by Studio settings + auth pages via tenants-client.
  • via NEXT_PUBLIC_VOICE_SFU_URL
    WebRTC recv-only Voice SFU — streams the agent's TTS audio into the OBS Browser Source page via VoiceClient from @openalicelabs/voice-client. BGM BgmPlayer ducks volume on oa-voice-chunk events.
Consumed env
  • BROWSE_INTERNAL_URL
  • BROWSE_INTERNAL_SECRET
  • PTY_INTERNAL_URL
  • PTY_INTERNAL_SECRET
  • PTY_PUBLIC_WS_BASE
  • NEXT_PUBLIC_API_URL
  • NEXT_PUBLIC_BGM_PRESETS
  • NEXT_PUBLIC_CDN_URL
  • NEXT_PUBLIC_DEFAULT_VRM_URL
  • NEXT_PUBLIC_DEFAULT_DOMAIN
  • NEXT_PUBLIC_LIVE_CONTROL_URL
  • NEXT_PUBLIC_OA_LOG_LEVEL
  • NEXT_PUBLIC_RADIO_URL
  • NEXT_PUBLIC_RT_URL
  • NEXT_PUBLIC_SCENE_CONTROL_WS_URL
  • NEXT_PUBLIC_TENANTS_URL
  • NEXT_PUBLIC_VOICE_SFU_URL
Exposes
  • http_endpoint: https://live.blal.pro
  • http_endpoint: https://live.blal.pro/scene/[user]
  • http_endpoint: https://live.blal.pro/studio
  • rest_api: POST /api/browse-session
  • rest_api: POST /api/pty-session
  • rest_api: GET /api/health
Events published
  • oa-voice-chunk
  • oa-voice-finished
  • oa-bgm-loop
  • oa-sfx-subscribe
  • oa-sfx-caption
Events consumed
  • oa-voice-chunk
  • oa-voice-finished
  • oa-bgm-loop
  • oa-sfx-subscribe
  • oa-sfx-caption
Env drift · manifest ⇄ code

Atlas grepped the repo's source for env reads (rust env::var · ts process.env · py os.environ · shell $VAR) and diffed against the manifest's consumes_env. Two lists below: stale declarations and undeclared reads.

Code reads, manifest omits
  • BROADCASTS_DB_URL
  • BROWSE_URL
  • CHAT_BRIDGE_DB_URL
  • CHROMIUM_FLAGS
  • DATABASE_URL
  • INTERNAL_BROADCAST_SECRET
  • INTERNAL_PERSONA_URL
  • INTERNAL_RADIO_URL
  • KICK_PUSHER_URL
  • LIVE_CONTROL_PORT
  • LIVE_CONTROL_WATCHDOG_INTERVAL_SECS
  • NEXT_PUBLIC_HUB_BASE_URL
  • NEXT_PUBLIC_LIVE_URL
  • NEXT_PUBLIC_LOUNGE_BASE
  • NEXT_PUBLIC_PERSONA_BFF_BASE
  • NEXT_PUBLIC_PERSONA_PUBLIC_URL
  • OA_AGENT_DISPATCH_URL
  • OA_AGENT_ID
  • OA_AUDIO_BITRATE
  • OA_AUDIO_DEVICE
  • OA_AUTH_BASE
  • OA_AUTH_URL
  • OA_AUTO_DISCOVER
  • OA_AUTO_UPDATE
  • OA_BROADCAST
  • OA_BROADCAST_ID
  • OA_CONFIG
  • OA_DAEMON_POLL_SECS
  • OA_DISABLE_AUDIO
  • OA_FFMPEG_PATH
  • OA_FRAMERATE
  • OA_FULLSCREEN
  • OA_HEIGHT
  • OA_HUB_BASE
  • OA_INTERNAL_SECRET
  • OA_KICK_SLUG
  • OA_LIVE_CONTROL_BASE
  • OA_LIVE_CONTROL_URL
  • OA_OBSERVABILITY_PORT
  • OA_PERSONA_URL
  • OA_PRESET_ID
  • OA_QUALITY_PRESET
  • OA_RESOLUTION
  • OA_SCENE
  • OA_SCENE_BASE
  • OA_SCENE_URL
  • OA_STREAMHOST_MAX_RSS_MB
  • OA_TENANT_ID
  • OA_TIKTOK_SIGN_API_KEY
  • OA_TIKTOK_USERNAME
  • OA_TOKEN
  • OA_TTS_PROVIDER
  • OA_TWITCH_BOT_LOGIN
  • OA_TWITCH_BOT_USER_ID
  • OA_TWITCH_BROADCASTER_ID
  • OA_TWITCH_CHANNEL
  • OA_TWITCH_CLIENT_ID
  • OA_TWITCH_OAUTH
  • OA_USER
  • OA_USER_ID
  • OA_VIDEO_BITRATE
  • OA_VIDEO_ENCODER
  • OA_WIDTH
  • OA_YT_API_KEY
  • OA_YT_LIVE_CHAT_ID
  • OPENALICE_AUTH_URL
  • OTEL_EXPORTER_OTLP_ENDPOINT
  • OTEL_SERVICE_NAME
  • PLAYWRIGHT_BASE_URL
  • PTY_URL
  • PUBLIC_SCENE_URL_TEMPLATE
  • PULSE_RUNTIME_PATH
  • SCHEDULER_URL
  • SHARED_JWT_SECRET
  • STREAMING_TTS
  • TENANT_RATE_LIMIT_PER_MINUTE
  • VOICE_CACHE_DIR
  • VOICE_PUBLIC_BASE
  • VOICE_URL
  • YOUTUBE_OAUTH_CLIENT_ID
  • YOUTUBE_OAUTH_CLIENT_SECRET
  • YOUTUBE_OAUTH_REDIRECT_URL
Observed reads: 99 · declared: 17

Roadmap · what's planned next

29% avg5 goals

Code composition

tokei · loc
89.8kloc
  • TSX54.2%
  • Rust23.2%
  • TypeScript20.0%
  • Shell1.2%
  • Python0.6%
  • Other0.9%
  • TSX54.2%· 48,628
  • Rust23.2%· 20,793
  • TypeScript20.0%· 17,945
  • Shell1.2%· 1,034
  • Python0.6%· 563
  • CSS0.4%· 316
  • JSON0.2%· 178
  • TOML0.1%· 131
  • +3 more0.2%· 196 · 11 files
Language% codecodecommentsblanksfiles
TSX54.2%48,6288,8553,376198
Rust23.2%20,7932,2181,63557
TypeScript20.0%17,9457,7291,952166
Shell1.2%1,03421115425
Python0.6%56321916
CSS0.4%31663292
JSON0.2%178004
TOML0.1%13110693
Dockerfile0.1%10289254
SQL0.1%525483
JavaScript0.0%42874
Markdown0.0%05,4501,31138

Telemetry ribbon · last 90 days

26 events
MAYJUNJUL2026-05-21 · commit · new commit 1be91e3 → 219edbd2026-05-21 · commit · new commit 219edbd → 70c59b12026-05-21 · commit · new commit 70c59b1 → f97db8b2026-05-21 · commit · new commit f97db8b → fda3a892026-05-21 · commit · new commit fda3a89 → 55b59ae2026-05-22 · commit · new commit 55b59ae → 2590ce32026-05-22 · commit · new commit 2590ce3 → 2ecc2ea2026-05-22 · commit · new commit 2ecc2ea → 88cb9d52026-05-23 · commit · new commit 88cb9d5 → 6a5c50b2026-05-23 · commit · new commit 6a5c50b → 148bba72026-05-23 · commit · new commit 148bba7 → a7673332026-05-23 · commit · new commit a767333 → ac300bd2026-05-23 · commit · new commit ac300bd → 99b794c2026-05-24 · commit · new commit 99b794c → ad71b6e2026-05-24 · commit · new commit ad71b6e → 938c7092026-05-25 · commit · new commit 938c709 → 1ebd7ca2026-06-01 · commit · new commit 1ebd7ca → 34376d12026-06-02 · commit · new commit 34376d1 → 7b941a82026-06-02 · commit · new commit 7b941a8 → 78f77252026-06-02 · commit · new commit 78f7725 → 0861e142026-06-03 · commit · new commit 0861e14 → a319e872026-06-17 · commit · new commit a319e87 → c645f802026-06-17 · commit · new commit c645f80 → b57c19a2026-06-17 · commit · new commit b57c19a → 9f736b82026-07-06 · commit · new commit 9f736b8 → aac755b2026-07-06 · commit · new commit aac755b → aab58c4NOW
commitmanifestlifecyclekind

app/account

37 features
  • app.account.agent
    live-hub/src/app/account/agent/edit/page.tsx:15
    Hub page for unified agent config — per-product enable + system prompt, shared identity (avatar + VRM), shared voice (TTS + STT). Read by every OpenAlice product at runtime.
    page:/account/agent
    since 0.5.0
  • app.account.agent.redirect
    live-hub/src/app/account/agent/page.tsx:17
    Legacy /account/agent path redirects to the new /account/agents list. The editor moved to /account/agent/edit.
    page:/account/agent → /account/agents
    since 0.11.0
  • app.account.agents.list
    live-hub/src/app/account/agents/page.tsx:17
    Hub agents list — one row per agent under this tenant, link to per-agent settings.
    page:/account/agents
    since 0.11.0
  • app.account.agents.settings
    live-hub/src/app/account/agents/[id]/settings/page.tsx:31
    Per-agent settings page — Identity, Soundboard toggle, Voice editor (voice_id + stability + similarity + speed + Test), Avatar editor (VRM picker + display_name + Use default), BGM source picker (None + 4 shared radio channels with now-playing strip). Voice + Avatar + BGM persist via persona-server's PATCH /v1/me/agents/:id/overrides.
    page:/account/agents/[id]/settings
    since 0.11.1
  • app.account.ai-moderation
    live-hub/src/app/account/ai-moderation/page.tsx:43
    Hub page for the layer-2 (AI) moderation engine — master toggle, confidence-threshold + budget tuning, undo / training-feedback panels. Companion of /account/youtube (layer-1 static rules).
    page:/account/ai-moderation
    since 0.13.0
  • app.account.chat-sources
    live-hub/src/app/account/chat-sources/page.tsx:96
    Hub page for managing per-platform live chat connections (YouTube / Twitch / Kick / TikTok) the chat-bridge service consumes. CRUD + masked preview + reveal-after-password.
    page:/account/chat-sources
    since 0.5.0
  • app.account.dashboard
    live-hub/src/app/account/page.tsx:4
    Logged-in account home — surfaces user identity, presence-status picker (online/idle/dnd/invisible), verify-email banner with resend, and the navigation grid into agent / streams / radio / sounds / chat / triggers sub-pages.
    since 0.4.0
  • app.account.devices
    live-hub/src/app/account/devices/page.tsx:26
    Hub page for managing paired openalice-stream-host installs. Generate pairing codes, list claimed devices, revoke. 90-day device JWTs scoped to live + persona only.
    page:/account/devices
    since 0.6.0
  • app.account.lounge
    live-hub/src/app/account/lounge/page.tsx:19
    Hub Lounge rooms list + per-room Mods entry. Lists rooms owned by the tenant, deep-links to persona-web for live experience, and to Mods tab for Phase 4 moderation primitives.
    page:/account/lounge
    since 0.11.0
  • app.account.lounge.audit
    live-hub/src/app/account/lounge/[id]/audit/page.tsx:19
    Per-room unified moderation audit log — bans/mutes/deletes/reports/mods/settings; mod-only.
    page:/account/lounge/[id]/audit
    since 0.13.0
  • app.account.lounge.mods
    live-hub/src/app/account/lounge/[id]/mods/page.tsx:20
    Per-room moderation panel — Phase 4 bans/mutes/mods/settings, owner-only grant picker.
    page:/account/lounge/[id]/mods
    since 0.11.0
  • app.account.lounge.reports
    live-hub/src/app/account/lounge/[id]/reports/page.tsx:24
    Per-room report queue — list open user reports, resolve via dismiss/delete/mute/ban actions; live WS subscription with REST fallback; mod-only.
    page:/account/lounge/[id]/reports
    since 0.12.0
  • app.account.radio
    live-hub/src/app/account/radio/page.tsx:11
    Hub Radio page — lists shared + private radio channels, shows what's playing now, lets the streamer create a private channel and set any channel as the agent's BGM source.
    page:/account/radio
    since 0.11.0
  • app.account.redemption-triggers
    live-hub/src/app/account/triggers/page.tsx:34
    Hub page for binding viewer redemptions (Twitch channel-points / YouTube SuperChat / Kick / TikTok) to live actions (play_sound / scene_change / gesture / tts_shout / expression). The Neuro-sama-tier audience-engagement loop.
    page:/account/triggers
    since 0.11.0
  • app.account.security
    live-hub/src/app/account/security/page.tsx:23
    TOTP setup + disable surface for the authenticated user. Renders an otpauth QR + 6-digit verify input + password-gated disable form.
    page:/account/security
    since 0.5.0
  • app.account.sessions
    live-hub/src/app/account/sessions/page.tsx:16
    Hub page listing the user's active refresh-token sessions with revoke buttons. Powered by /auth/sessions + /auth/sessions/:id.
    page:/account/sessions
    since 0.5.0
  • app.account.soundboard
    live-hub/src/app/account/sounds/page.tsx:29
    Hub Soundboard page — upload, tag, bind, test-fire SFX clips for the agent's `play_sound` tool. Powers the Neuro-sama-style soundboard.
    page:/account/sounds
    since 0.10.0
  • app.account.streams
    live-hub/src/app/account/streams/page.tsx:79
    Hub page for managing per-platform stream destinations the broadcaster service pushes to. CRUD + masked-list + reveal-after-password.
    page:/account/streams
    since 0.5.0
  • app.account.streams-chat-mod
    live-hub/src/app/account/streams/[id]/chat-mod/page.tsx:32
    Mod-only live view of YouTube chat for one broadcast. Per-message delete + timeout (10m/1h/24h) + ban actions. Polls chat-bridge's ring buffer via persona. Show-only-mod-flagged filter + pin-message toggle. Empty state when the streamer isn't live.
    page:/account/streams/[id]/chat-mod
    since 0.12.0
  • app.account.streams.chat-page
    live-hub/src/app/account/streams/chat/page.tsx:27
    Studio Chat Tab page mounting StudioChatTab. Polls /live/status to discover the active broadcast and renders a "go live first" idle state otherwise.
    page:/account/streams/chat
    since 0.13.0
  • app.account.streams.engagement-picker
    live-hub/src/app/account/streams/_components/EngagementPresetPicker.tsx:40
    Hub Studio picker that surfaces the @openalicelabs/presets engagement catalogue (qa-host / battle-chat / cozy-hangout / coding-stream / lecture-moderator / talk-show-panel). Writes the chosen id into agent_config.products.live.preset_id; the next /live/start defaults to it.
    component:EngagementPresetPicker
    since 0.11.0
  • app.account.streams.engagement-preset-fetch
    live-hub/src/lib/api/broadcasts.ts:197
    Read the streamer's saved engagement-preset id (qa-host / battle-chat / cozy-hangout / coding-stream / lecture-moderator / talk-show-panel) from agent_config.products.live.preset_id. Persists across sessions; the next /live/start picks it up automatically.
    since 0.11.0
  • app.account.streams.engagement-presets
    live-hub/src/lib/engagementPresets.ts:23
    Hub-side typed mirror of the @openalicelabs/presets engagement catalogue (qa-host / battle-chat / cozy-hangout / coding-stream / lecture-moderator / talk-show-panel). Drives the Studio preset picker; the streamer's selected id rides on agent_config.products.live.preset_id and is forwarded to live-control's /live/start request.
    component:EngagementPresetPicker
    since 0.11.0
  • app.account.streams.engagement-preset-save
    live-hub/src/lib/api/broadcasts.ts:222
    Persist the streamer's preferred engagement preset (qa-host / etc) into agent_config.products.live.preset_id via the SDK's putAgentConfig. Forward-compat unknown-keys ride-through means the field rides on top of the typed ProductAgentConfig shape without a backend migration.
    since 0.11.0
  • app.account.streams.host-health
    live-hub/src/app/account/streams/_components/HostHealthCard.tsx:32
    Per-broadcast Host Health card on /account/streams. Polls /api/streamhost/heartbeat every 15s and renders a RSS / disk / ffmpeg / RTMP / interruptions / last-frame summary plus a Reset host button.
    component:HostHealthCard
    since 0.13.1
  • app.account.streams.poll-manager
    live-hub/src/app/account/streams/_components/PollManager.tsx:31
    Hub poll management surface — Create modal, active poll panel with countdown + Close-early, history list. Mounts under /account/streams when a broadcast is live.
    component:PollManager
    since 0.12.0
  • app.account.streams.scene-preview
    live-hub/src/app/account/streams/_components/ScenePreviewButtons.tsx:55
    Two buttons in the Hub's stream-destinations page that open the live renderer scene URL (with agents) and a no-agent demo room for previewing without going on-air.
    component:ScenePreviewButtons
    since 0.6.0
  • app.account.streams.theme-picker
    live-hub/src/app/account/streams/_components/ThemePicker.tsx:69
    Studio-side picker for the per-stream visual theme. 7-preset gallery + custom-override editor (colors / fonts / texture / corners / shadow / motion) + live iframe preview. Persists to agent_config.products.live.branding.theme — the same JSONB the live page reads.
    component:ThemePicker
    since 0.6.0
  • app.account.streams.theme-picker.sfx
    live-hub/src/app/account/streams/_components/SfxPickerSection.tsx:25
    Hub-side picker section for the per-stream sound-effect engine. Master toggle + volume + 5-preset gallery (silent / subtle-warm / arcade / sci-fi / nature) + per-event URL & volume overrides. Persists into branding.theme.sfx — the same JSONB the live page's SfxPlayer reads.
    component:SfxPickerSection
    since 0.9.0
  • app.account.streams.theme-quick-swap
    live-hub/src/app/account/streams/_components/ThemeQuickSwap.tsx:72
    Mid-stream theme flip UI — compact preset chips that fire a transient `branding.theme` payload onto the live-control bus so the streamer can change the look mid-broadcast without editing the persistent agent_config. Shows a banner when the live theme differs from the saved one.
    component:ThemeQuickSwap
    since 0.6.0
  • app.account.streams.theme-save
    live-hub/src/lib/api/broadcasts.ts:131
    Persist the per-stream theme into agent_config.products.live.branding.theme via the SDK's putAgentConfig — preserves every other live-product field on the same object.
    since 0.6.0
  • app.account.youtube
    live-hub/src/app/account/youtube/page.tsx:53
    Hub page for connecting a YouTube channel (OAuth youtube.readonly + youtube.force-ssl), inspecting granted scopes, and toggling per-tenant mod policies. Companion of /account/streams/[id]/chat-mod. Drives the YT Tier 1 wave Hub UI surface.
    page:/account/youtube
    since 0.12.0
  • app.account.youtube.audit-feed
    live-hub/src/app/account/youtube/page.tsx:773
    Hub card surfacing the persona oauth_audit_log feed for the calling tenant on /account/youtube. Renders op + status + ts + truncated IP. Powered by GET /v1/me/youtube/audit.
    component:AuditFeedCard
    since 0.13.0
  • app.account.youtube.auto-mod
    live-hub/src/app/account/youtube/auto-mod/page.tsx:50
    Hub page for the static auto-moderation rule set + shared mod-action audit log. Companion of /account/youtube + /account/ai-moderation.
    page:/account/youtube/auto-mod
    since 0.13.0
  • app.account.youtube.helpers
    live-hub/src/app/account/youtube/helpers.ts:8
    Time / IP formatters used by the YouTube settings page (expires-in countdown, "last refreshed" indicator, audit-feed IP truncator).
    function:formatExpiresInSecondsfunction:secondsUntilfunction:truncateIp
    since 0.13.0
  • app.studio.chat.send
    live-hub/src/lib/api/chat.ts:167
    Hub-side wrapper around live-control `/live/chat-out`. POSTs a JWT-authed chat reply to the per-broadcast chat-bridge fan-out so the streamer can reply to Twitch/YouTube/Kick chat from inside the Studio Chat tab.
    since 0.13.0
  • app.studio.chat-tab
    live-hub/src/app/account/streams/_components/StudioChatTab.tsx:98
    Studio Hub chat tab — subscribes to /auth/ws for cross-platform chat (Twitch/YouTube/Kick/TikTok) and writes back via live-control /live/chat-out so streamers monitor + reply without leaving the Hub.
    component:StudioChatTabpage:/account/streams/chat
    since 0.13.0

app/api

7 features
  • app.api.internal.streamhost.heartbeat
    live-hub/src/app/api/internal/streamhost/heartbeat/route.ts:15
    Internal-secret-gated POST receiver for the openalice-stream-host daemon's 30s heartbeat. Stashes the latest payload per (user_id, host_id) so /account/streams can render a Host Health card.
    route:POST /api/internal/streamhost/heartbeatroute:GET /api/internal/streamhost/heartbeat
    since 0.13.1
  • app.api.persona-bff
    live-hub/src/app/api/persona/[...path]/route.ts:40
    Catch-all reverse proxy from openalice-app to internal persona-server. Forwards Authorization + streams multipart bodies; passthrough for status + headers. Same-origin to the Hub so CORS is moot.
    route:GET /api/persona/*route:POST /api/persona/*route:PATCH /api/persona/*route:DELETE /api/persona/*route:PUT /api/persona/*
    since 0.7.0
  • app.api.radio-bff
    live-hub/src/app/api/radio/[...path]/route.ts:10
    Catch-all reverse proxy from openalice-app to internal openalice-radio. Forwards Authorization + passes JSON bodies through. Same-origin to the Hub so CORS is moot.
    route:GET /api/radio/*route:POST /api/radio/*route:PATCH /api/radio/*route:DELETE /api/radio/*
    since 0.11.0
  • app.api.streamhost.heartbeat-read
    live-hub/src/app/api/streamhost/heartbeat/route.ts:36
    Browser-facing JWT-gated read for the stream-host heartbeat snapshot. Returns hosts for the calling user (sub claim) by reading the same in-memory store the internal POST receiver writes to.
    route:GET /api/streamhost/heartbeat
    since 0.13.2
  • app.api.youtube-oauth-callback
    live-hub/src/app/api/youtube/oauth/callback/route.ts:40
    Completes the YouTube OAuth round-trip server-side — state check, code exchange, channel snapshot, signed handoff cookie, redirect to /account/youtube?just_connected=1. Client-side /complete finishes the persona POST.
    route:GET /api/youtube/oauth/callback
    since 0.12.1
  • app.api.youtube-oauth-complete
    live-hub/src/app/api/youtube/oauth/complete/route.ts:31
    Client-side completion of the YT OAuth round-trip — reads the handoff cookie set by /callback, verifies its signature, forwards to persona /v1/me/youtube/oauth-tokens with the user's Bearer JWT.
    route:POST /api/youtube/oauth/complete
    since 0.12.1
  • app.api.youtube-oauth-start
    live-hub/src/app/api/youtube/oauth/start/route.ts:41
    Builds Google OAuth URL with youtube.readonly + youtube.force-ssl + youtube scopes, stashes CSRF state cookie, redirects to Google. Companion of /api/youtube/oauth/callback. Drives /account/youtube Connect button + the stream-key auto-fetch flow.
    route:GET /api/youtube/oauth/start
    since 0.12.0

app/auth

5 features
  • app.auth.forgot-password
    live-hub/src/app/forgot-password/page.tsx:25
    Public form that requests a password-reset link by email.
    page:/forgot-password
    since 0.1.0
  • app.auth.login
    live-hub/src/app/login/page.tsx:28
    Public login + signup form with two-leg MFA prompt. Honors
    page:/login
    since 0.1.0
  • app.auth.reset-password
    live-hub/src/app/reset-password/page.tsx:22
    Set a new password using a single-use reset token.
    page:/reset-password
    since 0.1.0
  • app.auth.safe-return-url
    live-hub/src/lib/safe-return-url.ts:2
    Validate `?return=<url>` query params for post-login redirects.
    lib:safe-return-url
    since 0.2.0
  • app.auth.verify-email
    live-hub/src/app/verify/page.tsx:21
    Verify the email-verification token and bounce to /account.
    page:/verify
    since 0.1.0

app/component

1 feature
  • app.component.font-picker
    live-hub/src/app/account/streams/_components/FontPicker.tsx:163
    Custom-font gallery — list, upload (drag-drop + file picker), rename, delete with confirm, "Use as display/body/mono" hooks for the Theme picker.
    component:FontPicker
    since 0.6.0

app/components

1 feature
  • app.components.confirm-dialog
    live-hub/src/components/ConfirmDialog.tsx:33
    Shared async confirm() dialog + useConfirm() hook. Replaces native window.confirm() across the Hub.
    component:ConfirmDialoghook:useConfirmcomponent:ConfirmProvider
    since 0.14.0

app/hub

2 features
  • app.hub.launcher
    live-hub/src/app/page.tsx:82
    Top-level OpenAlice ecosystem launcher tiling Alice, Social, World, Persona, Live, wrapped in the shared EcosystemShell.
    page:/
    since 0.1.0
  • app.hub.online-users
    live-hub/src/app/_components/OnlineUsers.tsx:21
    Hub home widget listing users currently online across the ecosystem (last_seen ≤ 2 min, status ≠ invisible). Polls every 30s.
    component:OnlineUsers
    since 0.5.0

app/lib

23 features
  • app.lib.access-token
    live-hub/src/lib/access-token.ts:39
    Verify openalice-auth-issued access JWTs on the Hub. Used by routes that need a local user_id from the bearer without forwarding to persona.
    function:verifyAccessTokenfunction:extractBearerclass:AccessTokenError
    since 0.13.2
  • app.lib.agents-api
    live-hub/src/lib/agentsApi.ts:24
    Typed client for /v1/me/agents on persona-server. Powers the AgentPicker dropdown and the per-agent settings page.
    function:listAgentsfunction:getAgent
    since 0.11.0
  • app.lib.ai-mod-api
    live-hub/src/lib/aiModApi.ts:26
    Typed client for the AI moderation REST surfaces on persona-server. Powers `/account/ai-moderation` settings + pending-review + training-feedback panels.
    function:fetchAiModSettingsfunction:patchAiModSettingsfunction:fetchAiModActionsfunction:undoAiModActionfunction:fetchAiModFeedbackfunction:submitAiModFeedback
    since 0.13.0
  • app.lib.api-client
    live-hub/src/lib/api-client.ts:47
    Shared apiFetch() helper — Bearer header + JSON parse + error normalization. Backs every src/lib/*Api.ts client.
    function:apiFetchfunction:personaApiFetchclass:ApiErrorclass:NotSignedInError
    since 0.12.0
  • app.lib.audio-api
    live-hub/src/lib/audioApi.ts:37
    Typed client for /v1/me/audio CRUD on persona-server — Bearer JWT, multipart upload, JSON rename, public URL decoration. Powers the Hub Music & Audio picker.
    function:listAudiofunction:uploadAudiofunction:renameAudiofunction:deleteAudio
    since 0.10.0
  • app.lib.fonts-api
    live-hub/src/lib/fontsApi.ts:37
    Typed client for /v1/me/fonts CRUD on persona-server — Bearer JWT, multipart upload, JSON rename, public URL decoration.
    function:listFontsfunction:uploadFontfunction:renameFontfunction:deleteFont
    since 0.6.0
  • app.lib.forwarded-origin
    live-hub/src/lib/forwarded-origin.ts:31
    Reconstructs the public origin (proto://host) from a NextRequest by honouring NEXT_PUBLIC_HUB_BASE_URL + X-Forwarded-Host/Proto, falling back to nextUrl.origin. Fixes 0.0.0.0:3203 leaking into OAuth redirects.
    function:publicOrigin
    since 0.12.1
  • app.lib.idle-config-api
    live-hub/src/lib/idleConfigApi.ts:23
    Typed client for /v1/me/agents/:id/idle-config — backs the Hub's Idle behaviour section.
    function:getIdleConfigfunction:patchIdleConfig
    since 0.11.2
  • app.lib.live-scenes
    live-hub/src/lib/liveScenes.ts:24
    Canonical scene-mode catalog for the Hub. Mirrors openalice-live/SCENE_REGISTRY. Drives "Allowed scenes" config + channel-point trigger validation.
    const:LIVE_SCENESconst:LIVE_SCENE_IDSconst:DEFAULT_LIVE_SCENE_IDfunction:isValidLiveSceneId
    since 0.14.0
  • app.lib.lounge-api
    live-hub/src/lib/loungeApi.ts:11
    Hub-side typed client for Discord-Style Lounge — listing rooms + Phase-4 moderation primitives (bans, mutes, mods, settings).
    function:listMyRoomsfunction:getModerationSnapshotfunction:grantModeratorfunction:revokeModeratorfunction:unbanUserfunction:unmuteUserfunction:listReportsfunction:resolveReportfunction:listAuditfunction:setSlowModefunction:setModOnlyMode
    since 0.11.0
  • app.lib.lounge-reports-ws
    live-hub/src/lib/loungeReportsWs.ts:59
    Live WS subscription client for the Hub Reports tab — opens the chat WS, sends subscribe_mods after joined, surfaces report_received/report_resolved, reconnects with exponential backoff, signals fallback after 3 failures/60s, 30s heartbeat ping.
    function:openLoungeReportsWs
    since 0.13.0
  • app.lib.music-gen-api
    live-hub/src/lib/musicGenApi.ts:29
    Typed client for /v1/me/audio/generate (AI music generation via Lyria 3). Same Bearer JWT + same-origin BFF transport as audioApi. Powers the Hub generate page.
    function:generateMusicfunction:getMusicGenJobfunction:listMusicGenJobs
    since 0.11.0
  • app.lib.polls-api
    live-hub/src/lib/pollsApi.ts:24
    Typed client for /v1/me/polls (engagement polls). Powers the Hub Create-poll modal + active poll panel + history list.
    function:createPollfunction:closePollfunction:listPollsfunction:getPoll
    since 0.12.0
  • app.lib.redemption-triggers-api
    live-hub/src/lib/redemptionTriggersApi.ts:37
    Typed client for /v1/me/redemption-triggers CRUD on persona-server. Powers the Hub Channel-Point Triggers page where streamers bind viewer redemptions to live actions.
    function:listRedemptionTriggersfunction:createRedemptionTriggerfunction:patchRedemptionTriggerfunction:deleteRedemptionTrigger
    since 0.11.0
  • app.lib.soundboard-settings-api
    live-hub/src/lib/soundboardSettingsApi.ts:22
    Typed client for the per-agent soundboard kill switch on persona-server. Powers the Hub agent-settings toggle.
    function:listSoundboardSettingsfunction:setSoundboardEnabled
    since 0.10.1
  • app.lib.sounds-api
    live-hub/src/lib/soundsApi.ts:31
    Typed client for /v1/me/sounds CRUD + play on persona-server. Powers the Hub Soundboard page where streamers upload + tag SFX for the agent's `play_sound` tool.
    function:listSoundsfunction:uploadSoundfunction:patchSoundfunction:deleteSoundfunction:testFireSound
    since 0.10.0
  • app.lib.static-mod-api
    live-hub/src/lib/staticModApi.ts:22
    Typed client for the static-mod REST surface on persona-server. Powers `/account/youtube/auto-mod` Static rules tab + Action log tab.
    function:listStaticModRulesfunction:createStaticModRulefunction:patchStaticModRulefunction:deleteStaticModRulefunction:fetchModActionsLog
    since 0.13.0
  • app.lib.use-auto-dismiss-error
    live-hub/src/lib/use-auto-dismiss-error.ts:31
    React hook that auto-dismisses an error string after a configurable timeout. Single source of truth for the Hub's auto-fading error banners.
    hook:useAutoDismissError
    since 0.14.0
  • app.lib.use-redirect-on-401
    live-hub/src/lib/use-redirect-on-401.ts:30
    React hook that redirects to /login?return=<path> when a page's error state indicates an expired session.
    hook:useRedirectOn401function:isUnauthenticatedError
    since 0.14.0
  • app.lib.voices-api
    live-hub/src/lib/voicesApi.ts:24
    Curated voice catalogue + browser-native TTS preview for the per-agent Voice editor. Lists ElevenLabs handles + the Default sentinel; preview uses SpeechSynthesisUtterance locally so sliders demo without burning ElevenLabs quota.
    function:listVoicesfunction:previewVoice
    since 0.11.1
  • app.lib.vrms-api
    live-hub/src/lib/vrmsApi.ts:18
    Typed client for the per-tenant VRM library on persona-server. Powers the Hub's per-agent Avatar picker.
    function:listVrms
    since 0.11.1
  • app.lib.youtube-api
    live-hub/src/lib/youtubeApi.ts:38
    Typed client for YT OAuth + mod-page surfaces on persona-server. Powers `/account/youtube` settings page + `/account/streams/[id]/chat-mod` mod view.
    function:fetchYouTubeOAuthfunction:disconnectYouTubefunction:fetchModPolicyfunction:patchModPolicyfunction:moderateDeletefunction:moderateBanfunction:fetchYtChatRecent
    since 0.12.0
  • app.lib.yt-handoff
    live-hub/src/lib/yt-handoff.ts:38
    HS256 JWT helpers (sign + verify) for the YouTube OAuth callback handoff cookie. Uses SHARED_JWT_SECRET + aud="oa-yt-handoff".
    function:signHandoffTokenfunction:verifyHandoffTokenconst:YT_HANDOFF_COOKIEconst:YT_HANDOFF_AUD
    since 0.12.1

app/radio

2 features
  • app.radio.api-client
    live-hub/src/app/account/radio/radioApi.ts:8
    Typed fetch wrappers (RadioChannel / RadioTrack / NowPlayingSnapshot) for the Hub's /api/radio/* BFF proxy — channel CRUD, track listing, and now-playing snapshots consumed by the radio picker page.
    since 0.4.0
  • app.radio.components
    live-hub/src/app/account/radio/_components.tsx:7
    Presentational pieces for the radio picker page — ChannelCard with now-playing badge and "Set as BGM" action, plus the supporting list / empty-state primitives consumed by /account/radio.
    since 0.4.0

app/sounds

1 feature
  • app.sounds.agent-picker
    live-hub/src/app/account/sounds/_components/AgentPicker.tsx:29
    Dropdown that scopes a soundboard entry to a specific agent (or leaves it tenant-wide); fetches agents via /api/persona/v1/me/agents with graceful "Loading…" and "tenant-wide only" fallback states.
    since 0.5.0

app/streamhost

1 feature
  • app.streamhost.heartbeat-store
    live-hub/src/app/api/internal/streamhost/heartbeat/_store.ts:9
    In-process record of the latest 30s heartbeat from each tenant's openalice-stream-host daemon — captures encoder, watchdog events, RSS, and stream-state so the Host Health card on /account/streams can render liveness without a database round-trip.
    since 0.5.0

app/streams/theme-picker

6 features
  • app.streams.theme-picker.avatar-render
    live-hub/src/app/account/streams/_components/_theme-picker/AvatarRenderSection.tsx:26
    Authoring panel for branding.theme.avatar_render — picks 3D / 2D-toon / 2D-flat render mode and exposes outline (width + colour), hologram (intensity + tint), and advanced chroma/scan/flicker/glow controls as a post-process chain over the persona VRM.
    since 0.6.0
  • app.streams.theme-picker.background
    live-hub/src/app/account/streams/_components/_theme-picker/BackgroundSection.tsx:24
    Scene backdrop authoring sub-panel — colour / image / video / preset modes plus particles (density + tint), drift (slow-pan / Ken Burns), and a parallax-layer editor; persisted into branding.theme.scene_background and rendered live by SceneBackgroundLayer.
    since 0.6.0
  • app.streams.theme-picker.custom-override
    live-hub/src/app/account/streams/_components/_theme-picker/CustomOverrideEditor.tsx:19
    Expand/collapse accordion that wraps the granular field editors (Colors / Typography / Texture / Chrome) and the four sub-section panels (Avatar render / Background / Music / SFX) inside the ThemePicker so streamers can override individual fields on top of a preset.
    since 0.6.0
  • app.streams.theme-picker.fields
    live-hub/src/app/account/streams/_components/_theme-picker/fields.tsx:16
    Shared presentational field primitives (FieldSection wrapper, labelled colour pickers, segmented controls, sliders) reused across every ThemePicker sub-panel; pure props-driven, no module-local state or I/O.
    since 0.6.0
  • app.streams.theme-picker.music-audio
    live-hub/src/app/account/streams/_components/_theme-picker/MusicAudioSection.tsx:31
    BGM authoring sub-panel of the theme picker — drives branding.theme.bgm (track select, volume, loop, fade-in/out, duck-during-TTS) and previews the chosen track via a user-gesture Play button so browser autoplay unlocks for the preview iframe.
    since 0.6.0
  • app.streams.theme-picker.preset-gallery
    live-hub/src/app/account/streams/_components/_theme-picker/PresetGallery.tsx:13
    16:9 thumbnail grid of the 7 coordinated stream-theme presets — clicking a card re-seeds the working theme to that preset and clears any prior overrides via the onPick callback.
    since 0.6.0

app/streams/theming

9 features
  • app.lib.streamThemes.shim
    live-hub/src/lib/streamThemes.ts:20
    Backwards-compat re-export shim — preserves the legacy `@/lib/streamThemes` import path after the per-theme split into `@/lib/themes/`. Forwards every type, preset, registry, and builder helper unchanged.
    since 0.6.0
  • app.lib.themes.barrel
    live-hub/src/lib/themes/index.ts:22
    Public catalogue surface for the streaming theme system — types, 7 stream presets, 3 avatar-render presets, 8 background presets, 5 SFX presets, and builder helpers.
    since 0.6.0
  • app.lib.themes.builder
    live-hub/src/lib/themes/builder.ts:11
    Stateless StreamTheme helpers — compactTheme strip-empty, encodeThemeForPreview URL-safe JSON, STREAM_THEME_FIELDS form-builder map, STREAM_FONT_OPTIONS curated font-family list.
    since 0.6.0
  • app.lib.themes.types.avatar-render
    live-hub/src/lib/themes/types/avatar-render.ts:13
    Persona-side post-processing contract — render-mode (3d / 2d-toon / 2d-flat), outline, hologram, chromatic aberration, scan-lines, CRT flicker, glow, toon-steps.
    since 0.6.0
  • app.lib.themes.types.bgm
    live-hub/src/lib/themes/types/bgm.ts:13
    Hub-side BGM config mirror — drives the Music & Audio picker and the live BgmPlayer auto-fade / ducking behaviour.
    since 0.6.0
  • app.lib.themes.types.custom-font
    live-hub/src/lib/themes/types/custom-font.ts:11
    Per-tenant uploaded font binding — family-name → woff2 URL. The live StreamThemeProvider emits one @font-face rule per entry.
    since 0.6.0
  • app.lib.themes.types.scene-background
    live-hub/src/lib/themes/types/scene-background.ts:10
    Hub-side scene-background contract — color / image / video / preset source, parallax sprites, particle overlay, and Ken-Burns / slow-pan drift.
    since 0.6.0
  • app.lib.themes.types.sfx
    live-hub/src/lib/themes/types/sfx.ts:17
    Hub-side SFX config + preset shape mirrors. Drives the picker's Sound Effects section and the live SfxPlayer event listeners.
    since 0.6.0
  • app.lib.themes.types.stream-theme
    live-hub/src/lib/themes/types/stream-theme.ts:15
    Core StreamTheme contract — the flat design-token bag every theme preset materialises and the Hub theme picker mutates.
    since 0.6.0

app/streams/theming/avatar-render

4 features
  • app.lib.themes.avatar-render.cinema-clean-3d
    live-hub/src/lib/themes/avatar-render-presets/cinema-clean-3d.ts:5
    Zero-cost 3D render-mode preset — no outline, no hologram. The "reset to default" option for the avatar-render picker.
    since 0.6.0
  • app.lib.themes.avatar-render.holo-cyan
    live-hub/src/lib/themes/avatar-render-presets/holo-cyan.ts:18
    Cyan hologram render preset — 2D-toon + white outline + 0.3 holo intensity + 1.5 Hz CRT flicker + 0.25 scan-line undertone. The transmission / sci-fi look.
    since 0.6.0
  • app.lib.themes.avatar-render.neuro-sama-classic
    live-hub/src/lib/themes/avatar-render-presets/neuro-sama-classic.ts:17
    Cel-shaded 2D-toon avatar render preset with #222222 ink outline + 4 toon-steps. The polished-VTuber default.
    since 0.6.0
  • app.lib.themes.avatar-render.registry
    live-hub/src/lib/themes/avatar-render-presets/index.ts:6
    3-preset avatar render-mode catalogue — cinema-clean-3d / neuro-sama-classic / holo-cyan + picker meta. Drives the AvatarRenderSection of ThemePicker.
    since 0.6.0

app/streams/theming/backgrounds

10 features
  • app.lib.themes.backgrounds.anime-classroom
    live-hub/src/lib/themes/backgrounds/anime-classroom.ts:4
    Slice-of-life classroom scene — static WebP base + a parallax cloud sprite (screen-blend, 0.7 opacity, 0.15 speed) + low-density cherry-blossom petals.
    since 0.6.0
  • app.lib.themes.backgrounds.aurora
    live-hub/src/lib/themes/backgrounds/aurora.ts:4
    Northern-lights scene — static WebP aurora + low-density pale-cyan stars + Ken-Burns drift. Night-sky / dreamy mood.
    since 0.6.0
  • app.lib.themes.backgrounds.beach-sunset
    live-hub/src/lib/themes/backgrounds/beach-sunset.ts:4
    Tropical-sunset scene — looping waves video + Ken-Burns slow drift, no particle overlay.
    since 0.6.0
  • app.lib.themes.backgrounds.cdn
    live-hub/src/lib/themes/backgrounds/cdn.ts:12
    CDN base-URL helper for bundled scene assets — `${NEXT_PUBLIC_CDN_URL ?? cdn.blal.pro}/scenes`. Imported by every bundled background preset.
    since 0.6.0
  • app.lib.themes.backgrounds.cozy-cafe
    live-hub/src/lib/themes/backgrounds/cozy-cafe.ts:4
    Coffee-shop interior scene background — looping video, slow-pan drift, no particle overlay.
    since 0.6.0
  • app.lib.themes.backgrounds.cyberpunk-city
    live-hub/src/lib/themes/backgrounds/cyberpunk-city.ts:4
    Cyberpunk neon-street scene — looping video + cool-cyan rain overlay at 0.6 density. No drift, ambient motion comes from the video.
    since 0.6.0
  • app.lib.themes.backgrounds.lofi-room
    live-hub/src/lib/themes/backgrounds/lofi-room.ts:4
    Cozy lofi-room scene background — looping video + warm-amber dust particles at 0.3 density, no drift. Chill / study stream mood.
    since 0.6.0
  • app.lib.themes.backgrounds.none
    live-hub/src/lib/themes/backgrounds/none.ts:4
    No-op background — solid theme.bg color, no particles, no drift. The "reset" option in the background picker.
    since 0.6.0
  • app.lib.themes.backgrounds.registry
    live-hub/src/lib/themes/backgrounds/index.ts:12
    8-preset scene-background catalogue + picker meta + segmented-control option lists (particles / drift / parallax-blend).
    since 0.6.0
  • app.lib.themes.backgrounds.space-stars
    live-hub/src/lib/themes/backgrounds/space-stars.ts:4
    Deep-space scene — solid #020208 base + white twinkling stars at 0.7 density + slow-pan drift. Sci-fi / coding-stream mood.
    since 0.6.0

app/streams/theming/presets

8 features
  • app.lib.themes.presets.editorial-print
    live-hub/src/lib/themes/presets/editorial-print.ts:4
    Newsprint stream theme — off-white canvas, brick-red accent, serif stack, grid pattern, hard shadows + static motion. The longform-essay mood.
    since 0.6.0
  • app.lib.themes.presets.glass-dark
    live-hub/src/lib/themes/presets/glass-dark.ts:4
    Vision-Pro inspired frosted-glass stream theme — pastel-purple primary, soft shadow, noise pattern, big 20px corner radius.
    since 0.6.0
  • app.lib.themes.presets.minimal-mono
    live-hub/src/lib/themes/presets/minimal-mono.ts:4
    Swiss-design stream theme — pure black on white, square corners, no shadow, static motion. Typography-portfolio / lecture mood.
    since 0.6.0
  • app.lib.themes.presets.pastel-bubblegum
    live-hub/src/lib/themes/presets/pastel-bubblegum.ts:4
    Kawaii / cozy stream theme — bubblegum pink + mint accent, rounded 24px corners, Quicksand display, playful motion + dots pattern.
    since 0.6.0
  • app.lib.themes.presets.registry
    live-hub/src/lib/themes/presets/index.ts:10
    7-preset stream-theme catalogue + picker meta. Drives the ThemePicker gallery + the resolveTheme helper that seeds defaults before custom overrides.
    since 0.6.0
  • app.lib.themes.presets.vintage-sepia
    live-hub/src/lib/themes/presets/vintage-sepia.ts:4
    Old-paper stream theme — sepia ink on cream, brick-orange accent, olive secondary, serif stack. The library / radio-show mood.
    since 0.6.0
  • app.lib.themes.presets.vtuber-neon-cyan
    live-hub/src/lib/themes/presets/vtuber-neon-cyan.ts:4
    Cyberpunk stream theme — electric cyan accent, magenta secondary, monospace stack, warp bg-pattern, hard shadows + playful motion.
    since 0.6.0
  • app.lib.themes.presets.vtuber-warm
    live-hub/src/lib/themes/presets/vtuber-warm.ts:4
    Atelier-default stream theme — warm amber accent on cream, soft shadow + noise pattern. The cozy long-form-stream default.
    since 0.6.0

app/streams/theming/sfx

7 features
  • app.lib.themes.sfx.arcade
    live-hub/src/lib/themes/sfx-presets/arcade.ts:4
    Arcade SFX preset — 8-bit blips, retro pops, coin pings, fanfare. Tuned for gaming / retro streams.
    since 0.6.0
  • app.lib.themes.sfx.cdn
    live-hub/src/lib/themes/sfx-presets/cdn.ts:6
    CDN base-URL helper for bundled SFX clips — `${NEXT_PUBLIC_CDN_URL ?? cdn.blal.pro}/audio/sfx`. Imported by every bundled SFX preset.
    since 0.6.0
  • app.lib.themes.sfx.nature
    live-hub/src/lib/themes/sfx-presets/nature.ts:4
    Nature SFX preset — water drops, wind, soft chimes, bird tweets. Tuned for cozy / lofi streams.
    since 0.6.0
  • app.lib.themes.sfx.registry
    live-hub/src/lib/themes/sfx-presets/index.ts:10
    5-preset SFX catalogue + 5-channel event meta + default volume constants. Drives the SfxPickerSection of ThemePicker and the live SfxPlayer event dispatch.
    since 0.6.0
  • app.lib.themes.sfx.sci-fi
    live-hub/src/lib/themes/sfx-presets/sci-fi.ts:4
    Sci-fi SFX preset — synth pulses, hologram zaps, deep bass thumps. Tuned for cyberpunk / hacker streams.
    since 0.6.0
  • app.lib.themes.sfx.silent
    live-hub/src/lib/themes/sfx-presets/silent.ts:4
    Silent SFX preset — empty URLs in every slot. The opt-out default so streamers who enable SFX but don't pick a preset get zero sound.
    since 0.6.0
  • app.lib.themes.sfx.subtle-warm
    live-hub/src/lib/themes/sfx-presets/subtle-warm.ts:4
    Subtle-warm SFX preset — quiet bell + soft pop + gentle whoosh + warm doorbell + light click. The lowest-distraction default for most streams.
    since 0.6.0

chat-bridge/aggregator

3 features
  • chat-bridge.aggregator.chat_feed
    live-chat-bridge/src/aggregator.rs:6
    FeedAggregator maintains a per-(platform, channel_id) 30-second rolling window of ChatMessage records. On each tick it emits a ChatFeedPayload to auth's /internal/chat-feed containing: message_count, participant_count, top-5 n-gram topics (unigram– trigram, English+Russian stopwords filtered), mood label (positive/negative/mixed/neutral via polarity lexicon), and 3 highlight quotes (longest distinct-author messages, ≤280 chars). Empty windows still emit — "room went quiet" is an agent signal. The tick loop is spawned by `spawn_tick_loop` and runs at the window interval on a separate Tokio task. Per-(platform, channel_id) we keep a rolling window of recent messages. Every tick we: 1. Drop messages older than the window. 2. Count n-grams across the remaining text → top topics. 3. Pick top-K verbatim messages (longest, distinct authors) → highlight quotes. 4. Sentence-level polarity-lexicon scan → mood (or `None` if we don't see enough signal). 5. POST a ChatFeedPayload to auth `/internal/chat-feed`. Empty windows are still emitted — "the room went quiet" is itself a signal the agent might want to act on.
  • chat-bridge.aggregator.feed-state
    live-chat-bridge/src/aggregator.rs:105
    FeedAggregator — per-(platform, channel_id) rolling 30s window of ChatMessage records. `record` appends + eagerly trims; `tick` builds the digest and POSTs to /internal/chat-feed. Holds the Mutex-guarded HashMap + window length + shared HTTP client.
    since 0.2.0
  • chat-bridge.aggregator.tick-loop
    live-chat-bridge/src/aggregator.rs:515
    spawn_tick_loop — Tokio task that fires FeedAggregator::tick at the window cadence (default 30s). Emits ChatFeedPayload (message_count + participant_count + top-5 ngram topics + mood + 3 highlight quotes) per channel even when empty so "room went quiet" is itself a signal.
    since 0.2.0

chat-bridge/binary

1 feature
  • chat-bridge.binary.entrypoint
    live-chat-bridge/src/main.rs:4
    Thin entrypoint — orchestrates init::* (tracing, chat-store, pipeline, alert + redemption sinks), platforms::* spawns (auto-discover + explicit-flag paths), and routes::spawn_obs_if_enabled. Behaviour-preserving split of the historic 1739-LOC main.rs.
    since 0.1.0

chat-bridge/boot

2 features
  • chat-bridge.boot.auto-discover
    live-chat-bridge/src/init.rs:331
    Auto-discovery client — GETs openalice-auth `/internal/users/{user}/chat-sources` at boot when OA_AUTO_DISCOVER=true. Each enabled chat-source row becomes a per-platform connector spawn (youtube / twitch / kick / tiktok) alongside the explicit OA_*_CHANNEL/USERNAME flags.
    since 0.3.0
  • chat-bridge.init.boot-helpers
    live-chat-bridge/src/init.rs:8
    Boot-time wiring helpers (tracing/OTel subscriber install, optional ChatStore connect, Pipeline + alert/redemption sink assembly, auto-discover client). Replaces the ~370-line bootstrap that used to live inline in main(). Behaviour preserved — same env-var contract, same log lines, same graceful-degradation semantics.
    since 0.4.0

chat-bridge/cli

1 feature
  • chat-bridge.cli.args
    live-chat-bridge/src/cli.rs:7
    clap-derived Args struct — single source of truth for every OA_* env-var the chat-bridge reads at boot. Splitting this out of main.rs keeps the binary entrypoint a thin wiring layer and makes the env-contract grep-friendly.
    since 0.4.0

chat-bridge/contract

1 feature
  • chat-bridge.contract.message-to-connector-event
    live-chat-bridge/src/sink.rs:533
    Translates bridge-internal ChatMessage into openalice-agent-protocol ConnectorEvent::ChatMessage with stable source.product="live", source.platform, participant id="<platform>:<user_id>", badges + avatar. Cross-platform-stable contract for AgentRuntime consumers.
    since 0.2.0

chat-bridge/dispatch

6 features
  • chat-bridge.dispatch.pipeline
    live-chat-bridge/src/sink.rs:363
    Pipeline — fan-out aggregator owned by main.rs. `deliver(msg)` records into FeedAggregator window, fires AuthSink (/internal/chat), optional AgentDispatchSink (/internal/agent/dispatch), optional SceneChatSink (/internal/scene/chat), and fire-and-forget Postgres insert via ChatStore. Each sink is independent — failures are swallowed.
    since 0.2.0
  • chat-bridge.dispatch.pipeline_fanout
    live-chat-bridge/src/sink.rs:5
    Pipeline::deliver tees every inbound ChatMessage into five concurrent sinks: (1) AuthSink → POST /internal/chat → /auth/ws agent fan-out; (2) FeedAggregator rolling-window record; (3) AgentDispatchSink → POST /internal/agent/dispatch (per-message AgentRuntime turn); (4) SceneChatSink → POST /internal/scene/chat (vrm-corner overlay); (5) fire-and-forget Postgres INSERT. Static-mod runs BEFORE all sinks so audit rows are durable even if a downstream sink fails. All sinks are fire-and-forget — a failed delivery logs `warn!` and never tears down the relay. @feature chat-bridge.dispatch.agent_dispatch AgentDispatchSink translates each ChatMessage into openalice-agent-protocol's ConnectorEvent::ChatMessage and POSTs it to live-control's /internal/agent/dispatch. Source fields (`product`, `platform`, `channel_id`) are stable across all platforms so every AgentRuntime consumer sees the same event shape regardless of origin. Participant id is prefixed `<platform>:<user_id>` to prevent cross-platform id collisions.
  • chat-bridge.sink.agent-dispatch
    live-chat-bridge/src/sink.rs:284
    AgentDispatchSink — POSTs `{broadcast_id, event}` to live-control's `/internal/agent/dispatch` (or social-api's equivalent) so the broadcast-owner's AgentRuntime turn fires per chat message. Service-to-service, X-Internal-Secret gated.
    http_client:/internal/agent/dispatch
    since 0.2.0
  • chat-bridge.sink.auth
    live-chat-bridge/src/sink.rs:39
    AuthSink — POSTs every normalized ChatMessage to openalice-auth's `/internal/chat` endpoint with X-Internal-Secret. Auth fans the message out to the broadcast owner's `/auth/ws` clients. `try_deliver` swallows + logs errors so a single bad request can't tear the bridge down.
    http_client:/internal/chat
    since 0.1.0
  • chat-bridge.sink.scene-alerts
    live-chat-bridge/src/sink.rs:197
    SceneAlertSink — POSTs AlertEvent (kind ∈ follow/sub/resub/gift_sub/cheer/raid/donation/tiktok_gift) to live-control's `/internal/scene/alert`. Drives the Streamlabs-style animated overlay card across Twitch, YouTube, Kick, TikTok.
    http_client:/internal/scene/alert
    since 0.3.0
  • chat-bridge.sink.scene-chat
    live-chat-bridge/src/sink.rs:103
    SceneChatSink — fan-out of ChatMessage to live-control's `/internal/scene/chat` for the OBS vrm-corner-chat overlay. Stamps platform-coded author colors (twitch #A78BFA, youtube #F87171, kick #22C55E, tiktok #FE2C55).
    http_client:/internal/scene/chat
    since 0.3.0

chat-bridge/http

5 features
  • chat-bridge.http.gdpr-export
    live-chat-bridge/src/routes.rs:471
    GET /internal/me/{tenant_id}/export.json — GDPR fan-out target hit by openalice-tenants' export-job worker. Two modes: persistence enabled → real `persisted_messages_count` + per-broadcast counts + recent 5000-row slice projected via ChatMessageRow::into_public; persistence disabled → stub body with explanatory note.
    http:/internal/me/{tenant_id}/export.json
    since 0.4.0
  • chat-bridge.http.observability-server
    live-chat-bridge/src/routes.rs:588
    Tiny axum server hosting GET /health (liveness JSON), GET /metrics (Prometheus chat_bridge_up / connectors_active / senders_registered gauges), POST /v1/send, POST /v1/moderate, GET /internal/me/{tenant_id}/export.json. Bound to 0.0.0.0:OA_OBSERVABILITY_PORT (default 3978; 0 disables).
    http:/healthhttp:/metrics
    since 0.2.0
  • chat-bridge.http.routes-module
    live-chat-bridge/src/routes.rs:10
    Dedicated routes module — owns ObsState, every axum handler, the router builder, and the spawn helper. Splitting this out of main.rs keeps the binary entrypoint a thin wiring layer and makes the HTTP surface grep-friendly.
    since 0.4.0
  • chat-bridge.http.v1-moderate
    live-chat-bridge/src/routes.rs:306
    POST /v1/moderate — X-Internal-Secret-gated moderation relay (ban / timeout / delete / unban). Validates msg_id with [a-zA-Z0-9-]{1..=64} gate (IRC tag-injection guard), strips Twitch's `twitch:` id prefix, hard-skips YouTube/Kick (no OAuth scopes), and queues platform-side via ModeratorCommand.
    http:/v1/moderate
    since 0.3.0
  • chat-bridge.http.v1-send
    live-chat-bridge/src/routes.rs:119
    POST /v1/send — X-Internal-Secret-gated chat write-back endpoint. Maps the agent's chat_write ToolCall to platform-specific PRIVMSG / liveChatMessages.insert sends. Sliding-window rate limiter per platform returns HTTP 429 + retry_after_seconds when every targeted platform is throttled. Fire-and-forget Postgres outbound insert on success.
    http:/v1/send
    since 0.3.0

chat-bridge/ingest

4 features
  • chat-bridge.ingest.kick_pusher
    live-chat-bridge/src/kick.rs:8
    Kick chat ingest over Pusher WebSocket (`chatrooms.<id>.v2`). Chatroom id resolved at boot via `kick.com/api/v1/channels/<slug>`. No auth required — public channels are readable without a token. Pusher envelope uses JSON-in-JSON (`data` field is a stringified JSON blob). Responds to both WS-level Ping/Pong and `pusher:ping` app-level keepalives. Reconnects with exponential backoff. Write-back is not implemented (returns 501). `chatroom_id` is per-channel and stable; you can look it up once via `https://kick.com/api/v1/channels/<slug>` (see `lookup_chatroom_id` below). The user supplies the channel slug; we resolve to chatroom_id at boot. Pusher protocol reference: Note: Kick's public chat WebSocket can drift in protocol details more than Twitch IRC. If bugs land, the typical fix is to re-derive the connection URL from the live page's network tab.
  • chat-bridge.ingest.tiktok_webcast
    live-chat-bridge/src/tiktok.rs:9
    TikTok Live ingest via community Webcast WebSocket + protobuf (`tiktoklive` crate v0.0.19). Signing delegated to eulerstream (free or paid tier via `OA_TIKTOK_SIGN_API_KEY`). Emits ChatMessage for WebcastChatMessage events and AlertEvent (kind="tiktok_gift") for WebcastGiftMessage events (diamond count × repeatCount). All other events (likes, joins, shares) are intentionally dropped. Read-only: TikTok write-back requires an invite-only official API. One connector per process (static HandlerBridge slot). Reconnects with exponential backoff + ±20% jitter. Wire flow: 1. HTTP fetch of `https://www.tiktok.com/@<username>/live` to resolve the active room id + signed connection params. 2. WebSocket connect to `wss://webcast<n>.us.tiktok.com/...`. 3. Receive protobuf `WebcastResponse` envelopes; each carries one or more typed `WebcastMessage` payloads. We route: - `WebcastChatMessage` → [`ChatMessage`] on the bus - `WebcastGiftMessage` → [`AlertEvent`] kind="tiktok_gift" - everything else (likes, joins, follows, …) is dropped. ## Operational fragility TikTok actively fights bot reads. Two things can break ingestion without code changes here: 1. **eulerstream signing service.** TikTok requires the WS URL to carry signed params minted by an internal service. The `tiktoklive` crate (and every community lib in its lineage) delegates this to a 3rd-party signer at <https://tiktok.eulerstream.com>. When that service is down or rate-limited, `client.connect().await` returns an error and we fall back into the reconnect-with-backoff loop. There is no manual recovery — the loop will resume the moment the signer comes back. Operator override: pass `OA_TIKTOK_SIGN_API_KEY` to upgrade to eulerstream's paid tier (higher rate, higher reliability); the env is read once at boot. 2. **Webcast schema changes.** TikTok adds / renames protobuf fields in lockstep with their web client roll-outs. The `tiktoklive` crate ships pre-generated message shapes, so a breaking schema change downstream of `0.0.19` (the latest published version, archived 2025-07) requires swapping the underlying lib. Mitigation is design-level: the connector surface (`run_tiktok`, [`TikTokChatPoller`]) is a thin shell so a future Option-C sidecar (Python `TikTokLive` exec'd as a child process speaking JSON over stdout) can drop in with zero call-site churn. ## Scope v0 is **read-only**. Posting bot replies back into a TikTok room requires their official OAuth-gated `/v2/chat` endpoint, which is invite-only and out of scope here. This module intentionally registers no outbound `ChatSender` so /v1/send callers see TikTok in `skipped` rather than queuing into a never-flushing mpsc.
  • chat-bridge.ingest.twitch_irc
    live-chat-bridge/src/twitch.rs:9
    Twitch IRC ingest over `wss://irc-ws.chat.twitch.tv:443`. Supports anonymous read (`justinfan*`) and authenticated read+write (OAuth `chat:read` + `chat:edit`). Parses PRIVMSG tags (roles, bits, display-name, tmi-sent-ts) into normalized ChatMessage. USERNOTICE lines produce AlertEvents (sub / resub / gift_sub / raid). Reconnects with exponential backoff (1s → 60s ± 20% jitter). @feature chat-bridge.writeback.twitch_irc Outbound write-back over the same IRC WebSocket. The agent's `chat_write` ToolCall flows through `/v1/send` → SenderRegistry → mpsc queue → PRIVMSG frame (≤500 chars, CR/LF stripped). Reply-parent threading via `@reply-parent-msg-id` tag. Moderation (ban / timeout / delete / unban) prefers Helix REST (`src/twitch_helix.rs`) with a 5s per-command timeout; falls back to IRC slash commands for envs without `OA_TWITCH_CLIENT_ID`. Anonymous read works: PASS = "SCHMOOPIIE" (any string), NICK = "justinfan12345". Authenticated read (recommended for prod, lifts rate limits + lets us send messages back) uses `oauth:<user_token>` from a Helix OAuth flow with `chat:read` + `chat:edit` scopes.
  • chat-bridge.ingest.youtube_polling
    live-chat-bridge/src/youtube.rs:10
    YouTube Live Chat ingest via `liveChatMessages.list` polling. Supports API-key auth (public chats) and OAuth (auto-discovers active broadcast, refreshes access token on 401). Poll interval is API-server-driven (`pollingIntervalMillis`, clamped ≥ 2s). Emits ChatMessage for textMessageEvent / superChatEvent / superStickerEvent. Alert fan-out: cheer (SuperChat/Sticker), sub/member_joined (newSponsorEvent), resub/member_milestone (memberMilestoneChatEvent), gift_sub/channel_sponsorship (membershipGiftingEvent). SuperChat/Sticker also route through RedemptionSink with a tier label ($1/$2/$5/$10/$20/$50/$100). Quota: each `liveChatMessages.list` call costs 5 quota units; Google's default daily budget is 10,000 units. A 1-hour stream polling every 5s eats ≈ 720 calls × 5 = 3600 units, which fits. Production deploys should request a quota raise.

chat-bridge/kick

3 features
  • chat-bridge.kick.chatroom-resolve
    live-chat-bridge/src/kick.rs:68
    Resolves a Kick channel slug to its numeric chatroom_id via kick.com/api/v1/channels/<slug>. Rejects non-browser user-agents so requests masquerade as Mozilla; 10s timeout.
    since 0.2.0
  • chat-bridge.kick.connector-loop
    live-chat-bridge/src/kick.rs:112
    Supervises one Kick Pusher WebSocket session — subscribes to `chatrooms.<id>.v2`, handles ping/pong (both WS-level and pusher-app-level keepalives), parses `App\\Events\\ChatMessageEvent` JSON-in-JSON envelopes, reconnects with exponential 1s→60s backoff. Read-only.
    since 0.2.0
  • chat-bridge.kick.poller-state
    live-chat-bridge/src/kick.rs:89
    KickChatPoller — per-broadcast state for one Kick public chat WebSocket connection (slug + resolved chatroom_id + broadcast_id stamp). Built by `for_slug` which performs the chatroom_id lookup.
    since 0.2.0

chat-bridge/observability

2 features
  • chat-bridge.tracing.otel-bootstrap
    live-chat-bridge/src/init.rs:105
    Opt-in OpenTelemetry OTLP exporter (gRPC tonic transport, opt-in via OTEL_EXPORTER_OTLP_ENDPOINT). Stamps service.name=openalice-chat-bridge + service.version from CARGO_PKG_VERSION as Resource attributes; registers the tracer globally so any direct OTel API caller lands on the same provider.
    since 0.4.0
  • chat-bridge.tracing.subscriber-install
    live-chat-bridge/src/init.rs:48
    Always-on fmt subscriber + opt-in OpenTelemetry OTLP layer (env-gated via OTEL_EXPORTER_OTLP_ENDPOINT). Returns silently on success; logs `warn!` when the OTel branch fails so the binary stays up on logs-only.
    since 0.4.0

chat-bridge/persistence

7 features
  • chat-bridge.chat-store.broadcast-counts
    live-chat-bridge/src/chat_store.rs:248
    GROUP BY broadcast_id row counter for a tenant, ordered by most-recent activity. Surfaced as `broadcasts: [{broadcast_id, message_count}]` in the GDPR export slice.
    since 0.4.0
  • chat-bridge.chat-store.connect
    live-chat-bridge/src/chat_store.rs:77
    Opens the PgPool, runs sqlx::migrate! to create chat_messages table/indexes idempotently, returns a ChatStore handle.
    since 0.4.0
  • chat-bridge.chat-store.handle
    live-chat-bridge/src/chat_store.rs:60
    ChatStore — Postgres-backed history handle around a sqlx::PgPool (max 8 connections, 5s acquire, 600s idle). Opt-in via CHAT_BRIDGE_DB_URL. Construction runs the bundled `migrations/0001_chat_messages.sql` idempotently. Cloned + pushed into tokio::spawn per delivery so the relay hot path never blocks on Postgres.
    since 0.4.0
  • chat-bridge.chat-store.insert-inbound
    live-chat-bridge/src/chat_store.rs:121
    Persist one inbound chat row (direction="inbound") with v7 UUID id, broadcast/tenant scope, platform-normalised name, sender_username + SHA-256 sender_id_hash (anonymisable analytics) + JSONB metadata (badges/paid/upstream_id). Hot-path safe — caller wraps in tokio::spawn.
    since 0.4.0
  • chat-bridge.chat-store.insert-outbound
    live-chat-bridge/src/chat_store.rs:156
    Persist one outbound chat row (direction="outbound") from the agent's `chat_write` ToolCall via /v1/send. Same row shape as inbound — gives a complete two-way replay for GDPR exports and overlay history.
    since 0.4.0
  • chat-bridge.chat-store.list-for-tenant
    live-chat-bridge/src/chat_store.rs:192
    Read DESC-by-received_at most-recent rows for a tenant_id (LIMIT clamped to ≤RECENT_EXPORT_LIMIT=5000). Powers the GDPR `/internal/me/{tenant}/export.json` recent-slice + the future /me/chat-history.json.gz streaming endpoint.
    since 0.4.0
  • chat-bridge.persistence.chat_store
    live-chat-bridge/src/chat_store.rs:10
    Postgres-backed chat_messages table (opt-in via CHAT_BRIDGE_DB_URL). ChatStore wraps a PgPool; sqlx::migrate! runs on boot. Each Pipeline::deliver inserts an inbound row and each /v1/send inserts an outbound row — both via fire-and-forget tokio::spawn so the relay hot path is never blocked by Postgres latency. Failure logs warn! and is swallowed. Rows carry: UUID v7 id (sortable + time-prefixed), broadcast_id, tenant_id, platform, sender_username, sender_platform_id, direction (inbound/outbound), text, metadata JSONB (badges, paid info, upstream_id). GDPR export reads counts + a recent 5000-row slice via ChatStore. The GDPR fan-out on tenants surfaced a stub `chat-bridge.json` slice with `persisted_messages_count: 0` and a note explaining the gap. This module backs the inbound + outbound write paths with a Postgres table (`chat_messages`) on the platform-db. Connection is opt-in via `CHAT_BRIDGE_DB_URL`: when unset, `ChatStore::open` returns `Ok(None)` and the per-broadcast bridge runs in its historic stateless mode (no INSERTs, GDPR export keeps surfacing the stub note). When set, `sqlx::migrate!()` brings the table up at boot and every Pipeline::deliver / handle_send call fires a row insert through `tokio::spawn`'d fire-and-forget tasks so the relay hot path never blocks on Postgres latency. Mirrors the shape of `openalice-live-control::broadcasts::BroadcastStore` intentionally — same `connect → migrate → insert/list` pattern, same best-effort failure semantics, same Postgres-gated integration test. Anyone fluent in the broadcasts store will recognise this one immediately.

chat-bridge/platforms

5 features
  • chat-bridge.platforms.dispatch-module
    live-chat-bridge/src/platforms/mod.rs:11
    Top-level platforms module — wraps the per-platform spawn arms (twitch, youtube, kick, tiktok). Each sub-module owns the auto-discovery + explicit-flag spawn paths and shares a SpawnCtx with the shared sinks (pipeline, alerts, redemption) + outbound sender registry.
    since 0.4.0
  • chat-bridge.platforms.kick
    live-chat-bridge/src/platforms/kick.rs:5
    Kick spawn dispatcher — resolves the channel slug to numeric chatroom_id via for_slug() then spawns crate::kick::run_kick. Resolve failures log + skip without taking the whole boot down.
    since 0.4.0
  • chat-bridge.platforms.tiktok
    live-chat-bridge/src/platforms/tiktok.rs:5
    TikTok Live spawn dispatcher — accepts either `username` (preferred) or `channel_name` (legacy) on the discovery row, strips the leading @ inside TikTokChatPoller::new, then spawns crate::tiktok::run_tiktok with the shared Pipeline + alert sink.
    since 0.4.0
  • chat-bridge.platforms.twitch
    live-chat-bridge/src/platforms/twitch.rs:7
    Twitch spawn dispatcher — wires IRC (TwitchChatPoller anonymous/authed) + Helix REST (moderation when OA_TWITCH_CLIENT_ID is set) + EventSub WS (follow alerts + channel-point redemptions when the full Helix tuple is present and OA_LIVE_CONTROL_URL is wired). Outbound + moderator mpsc channels are registered with the SenderRegistry only for authenticated connections.
    since 0.4.0
  • chat-bridge.platforms.youtube
    live-chat-bridge/src/platforms/youtube.rs:5
    YouTube Live spawn dispatcher — handles both OAuth (access_token + optional refresh_token + auto-discovered liveChatId) and api_key (paste-creds chat_id) flavours. Wraps crate::youtube::run_youtube, threading the shared Pipeline + alert/redemption sinks into the connector task.
    since 0.4.0

chat-bridge/rate-limit

2 features
  • chat-bridge.rate-limit.platform-cap-table
    live-chat-bridge/src/rate_limit.rs:46
    PlatformLimit — sliding-window cap config (max_messages + window Duration). Hardcoded per-platform table via `for_platform`: twitch 20/30s, youtube[_live] 5/60s, kick 10/60s, unknown 30/60s.
    since 0.3.0
  • chat-bridge.rate-limit.token-bucket
    live-chat-bridge/src/rate_limit.rs:85
    OutboundRateLimiter — DashMap<platform, Mutex<VecDeque<Instant>>> sliding-window buckets. try_acquire drops out-of-window timestamps, returns Err(retry_after) when full so /v1/send maps to HTTP 429 with retry_after_seconds.
    since 0.3.0

chat-bridge/redemption

2 features
  • chat-bridge.redemption.event-wire
    live-chat-bridge/src/redemption.rs:37
    RedemptionEvent — platform-agnostic wire shape POSTed to persona's `/internal/redemption-received`. Closed `platform` enum (twitch_points/youtube_superchat/kick_subscription/tiktok_gift) + redemption_key + viewer fields + optional amount/currency/message. Drives `{{viewer}}`/`{{amount}}` template substitution in tts_shout actions.
    since 0.5.0
  • chat-bridge.redemption.sink
    live-chat-bridge/src/redemption.rs:19
    Normalised RedemptionEvent + RedemptionSink that POSTs channel-point / SuperChat / Kick-sub / TikTok-gift events to persona's /internal/redemption-received so the dispatcher can match a redemption_triggers row and fire play_sound / scene_change / gesture / tts_shout / expression actions on the live page.
    since 0.5.0

chat-bridge/senders

3 features
  • chat-bridge.senders.moderator-command
    live-chat-bridge/src/senders.rs:62
    ModeratorCommand — enum of cross-platform moderation actions emitted by /v1/moderate (Ban/Timeout/DeleteMessage/Unban). Twitch routes via Helix REST (preferred) or legacy IRC slash commands; YouTube + Kick currently return "skipped" until OAuth scope sets land.
    since 0.3.0
  • chat-bridge.senders.outbound-chat-message
    live-chat-bridge/src/senders.rs:35
    OutboundChat — work item /v1/send pushes onto a connector's mpsc queue. Carries the text + optional reply_to (Twitch reply-parent thread id; YT/Kick ignore).
    since 0.3.0
  • chat-bridge.senders.registry
    live-chat-bridge/src/senders.rs:19
    Outbound chat + moderation sender registry — connector tasks register Arc<DashMap> handles at boot; the HTTP /v1/send and /v1/moderate routes look up the matching platform sender and forward the agent's chat_write or moderation ToolCall to Twitch / YouTube / Kick.
    since 0.3.0

chat-bridge/tiktok

2 features
  • chat-bridge.tiktok.connector-loop
    live-chat-bridge/src/tiktok.rs:180
    Supervises one TikTok community Webcast WebSocket session via the tiktoklive crate. Eulerstream-signed connect, sync→async HandlerBridge over a static OnceLock slot (one connector per process), routes OnChat→ChatMessage and OnGift→AlertEvent(kind="tiktok_gift"), drops likes/joins/shares. Reconnects with 1s→60s exponential backoff plus ±20% jitter.
    since 0.4.0
  • chat-bridge.tiktok.poller-state
    live-chat-bridge/src/tiktok.rs:120
    TikTokChatPoller — per-broadcast state (sanitised @handle without leading @, broadcast_id stamp, optional eulerstream paid-tier sign_api_key read from OA_TIKTOK_SIGN_API_KEY).
    since 0.4.0

chat-bridge/twitch

6 features
  • chat-bridge.twitch.connector-loop
    live-chat-bridge/src/twitch.rs:128
    Supervises one Twitch IRC WS session — CAP REQ tags+commands, PASS/NICK/JOIN, tokio::select multiplexes inbound PRIVMSG/USERNOTICE/PING, outbound mpsc queue (PRIVMSG up to 500 chars, CR/LF stripped, optional reply-parent-msg-id thread), and moderator-command queue (Helix REST preferred with 5s timeout → falls back to IRC slash). Exponential 1s→60s backoff + ±20% jitter on reconnect.
    since 0.1.0
  • chat-bridge.twitch.eventsub
    live-chat-bridge/src/twitch_eventsub.rs:41
    Twitch Helix EventSub WebSocket transport — subscribes to channel.follow v2 (plus other event types) over wss://eventsub.wss.twitch.tv/ws to close the follow-event gap IRC USERNOTICE can't cover; handles session_welcome / keepalive / reconnect / revocation envelopes.
    since 0.5.0
  • chat-bridge.twitch.eventsub-loop
    live-chat-bridge/src/twitch_eventsub.rs:93
    Long-running EventSub WS supervisor. Handles session_welcome (POSTs channel.follow v2 + optionally channel-points redemption subscriptions), session_keepalive, notification routing (follow → AlertEvent, channel_points_custom_reward_redemption → RedemptionEvent), session_reconnect (re-dial new shard with zero backoff), revocation (bail loud).
    since 0.5.0
  • chat-bridge.twitch.helix-client
    live-chat-bridge/src/twitch_helix.rs:51
    HelixClient — resolved Helix REST handle (oauth, client_id, broadcaster_id, moderator_id). Resolves logins → numeric ids at boot via GET /helix/users. `execute` routes ModeratorCommand to POST /moderation/bans (ban/timeout), DELETE /moderation/bans (unban), DELETE /moderation/chat (delete msg), validating logins ([a-z0-9_]{1,25}) and msg-ids ([a-zA-Z0-9-]{1,64}).
    since 0.4.0
  • chat-bridge.twitch.helix-moderation
    live-chat-bridge/src/twitch_helix.rs:28
    Helix REST client for Twitch moderation actions — wraps POST /helix/moderation/bans (ban/timeout), DELETE /helix/moderation/bans (unban), and DELETE /helix/moderation/chat (delete message) since Twitch deprecated the legacy IRC slash-command path in Feb 2023.
    since 0.4.0
  • chat-bridge.twitch.poller-state
    live-chat-bridge/src/twitch.rs:51
    TwitchChatPoller — per-channel auth state (channel, nick, pass). `new_anonymous` mints "justinfan<rand>" + "SCHMOOPIIE" pass for read-only public chat; `new_authed` formats `oauth:<token>` for read+write.
    since 0.1.0

chat-bridge/types

1 feature
  • chat-bridge.types.wire
    live-chat-bridge/src/types.rs:5
    Canonical ChatMessage / ChatAuthor / ChatPaid wire shape shared with openalice-auth::ws — the contract every connector normalises into before POSTing to /internal/chat. Drift here breaks the auth-side WebSocket fanout to the broadcast-owner agent.
    since 0.1.0

chat-bridge/writeback

1 feature
  • chat-bridge.writeback.rate_limiter
    live-chat-bridge/src/rate_limit.rs:9
    OutboundRateLimiter enforces per-platform sliding-window caps on agent write-back: Twitch 20/30s, YouTube 5/60s, Kick 10/60s, unknown 30/60s. Implemented as DashMap<platform, Mutex<VecDeque <Instant>>> — per-platform buckets avoid cross-platform contention. try_acquire returns Ok or Err(retry_after_duration) which the /v1/send handler maps to HTTP 429 + Retry-After. Acquisition is O(window_cap) on cleanup, negligible at live-chat message rates. The limiter uses a per-platform `VecDeque<Instant>` of recent send timestamps. On `try_acquire`, we drop timestamps older than the window, then check the remaining count against the cap. Below cap → record `now` and return Ok. At cap → return Err with the wait-until-next-slot duration so the caller can surface a Retry-After header. Caps (start conservative, tighten on real-world telemetry): * twitch — 20 msg / 30 s (Twitch IRC limit is 20/30s for non-mod broadcasters) * youtube — 5 msg / 60 s (YT live-chat throttles bots to ~10/min; we leave headroom) * kick — 10 msg / 60 s (no published limit; mirror YT) Lock granularity: one `Mutex<VecDeque<Instant>>` per platform key, hidden behind a DashMap so per-platform contention stays local. Acquire is bounded O(window_cap) on cleanup which is negligible at our message rates.

chat-bridge/youtube

8 features
  • chat-bridge.youtube.alert-parser
    live-chat-bridge/src/youtube.rs:442
    Detects YouTube monetisation / membership events on a liveChatMessages snippet and emits AlertEvent: superChatEvent/superStickerEvent → "cheer" (USD-equivalent dollars from amountMicros/1M), newSponsorEvent → "sub" (tier int from "Tier N" label), memberMilestoneChatEvent → "resub" (memberMonth), membershipGiftingEvent → "gift_sub" (giftMembershipsCount). giftMembershipReceivedEvent suppressed to avoid double-fire.
    since 0.3.0
  • chat-bridge.youtube.auth-flavor
    live-chat-bridge/src/youtube.rs:45
    YouTubeAuth enum — two flavours of YouTube credentials. ApiKey (public-chat read with operator-supplied live_chat_id) vs OAuth (auto-discovery of active broadcasts + refresh_token rotation).
    since 0.2.0
  • chat-bridge.youtube.connector-loop
    live-chat-bridge/src/youtube.rs:707
    Long-running YouTube Live Chat polling loop. Honours API-server-recommended `pollingIntervalMillis` (clamped ≥2s), fans chat messages to on_msg, alerts to SceneAlertSink, and SuperChat/Sticker redemptions to RedemptionSink in one pass. Exponential 1s→60s backoff on poll failure.
    since 0.2.0
  • chat-bridge.youtube.discover-live-chat
    live-chat-bridge/src/youtube.rs:84
    Auto-discovers the active broadcast's liveChatId via Data API liveBroadcasts.list?broadcastStatus=active. OAuth-only (API-key path returns None). Used at chat-bridge boot when liveChatId is absent from the config row.
    since 0.3.0
  • chat-bridge.youtube.oauth-refresh
    live-chat-bridge/src/youtube.rs:144
    Exchanges a refresh_token for a fresh access_token via Google's oauth2 token endpoint (grant_type=refresh_token). Mutates the YouTubeAuth::OAuth variant in place; triggered on 401 from liveChatMessages.list.
    since 0.3.0
  • chat-bridge.youtube.poll-cycle
    live-chat-bridge/src/youtube.rs:283
    One liveChatMessages.list page fetch — sends Authorization Bearer (OAuth) or &key= (API key), advances nextPageToken, transparently triggers refresh_oauth_token on 401, returns (chat_messages, alerts, redemption_inputs, next_interval). Costs 5 quota units per call.
    since 0.3.0
  • chat-bridge.youtube.poller-state
    live-chat-bridge/src/youtube.rs:236
    YouTubeChatPoller — per-broadcast polling state (HTTP client, YouTubeAuth, live_chat_id, broadcast_id stamp, page_token cursor across polls). Constructed via `new` (API key) or `with_auth` (OAuth).
    since 0.2.0
  • chat-bridge.youtube.superchat-redemption
    live-chat-bridge/src/youtube.rs:582
    Translates SuperChat/SuperSticker snippets into platform-agnostic RedemptionEvent for persona's dispatcher. Bins USD-equivalent dollars into seven tier labels ($1/$2/$5/$10/$20/$50/$100) so streamers bind one trigger per tier without exact-amount matching.
    since 0.5.0

hub/account

2 features
  • hub.account.audio.generate
    live-hub/src/app/account/audio/generate/page.tsx:21
    AI music generation page. Lyria 3 via OpenRouter, $0.08/song, $10/month default cap. Async job lifecycle with 5s polling, inline player + add-to-BGM hand-off, recent generations history.
    page:/account/audio/generate
    since 0.11.0
  • hub.account.edit
    live-hub/src/app/account/edit/page.tsx:10
    Edit display_name, bio, avatar_url, banner_url, pronouns. One profile across all 6 OpenAlice products.
    page:/account/edit
    since 0.3.0

live-broadcaster/entrypoint

1 feature
  • live-broadcaster.entrypoint.run
    live-broadcaster/entrypoint.sh:2
    entrypoint.sh — boots Xvfb framebuffer, launches headless Chromium pointed at SCENE_URL, starts ffmpeg with x11grab + tee output to every RTMP URL listed in RTMP_DESTINATIONS.
    script:entrypoint.sh
    since 0.1.0

live-broadcaster/image

1 feature
  • live-broadcaster.image.runtime
    live-broadcaster/Dockerfile:1
    Dockerfile — Debian-slim + Chromium + Xvfb + ffmpeg + pulseaudio. Builds the one-shot broadcaster image.
    file:Dockerfile
    since 0.1.0

live-control/persistence

1 feature
  • live-control.persistence.broadcast-store
    live-control/src/store.rs:3
    When DATABASE_URL is set, every broadcast row is inserted on /live/start and heartbeat-bumped every watchdog tick. On /live/stop the row is set to `stopped`. The schema includes a partial UNIQUE index on (user_id) WHERE status IN ('live','recovering') to enforce the one-live-broadcast-per- user invariant at the database level, mirroring the in-memory HashMap. Without DATABASE_URL the service runs in legacy in-memory-only mode — acceptable for local dev, not production. Lets the control plane survive a restart without losing track of in-flight broadcasts. The schema is intentionally narrow — every field is what the watchdog (`recovery.rs`) needs to decide whether to respawn the container pair or mark the broadcast as failed. ## Activation Set the `DATABASE_URL` environment variable to a reachable Postgres instance. When the variable is unset, the control plane falls back to the pre-resilience in-memory-only model — useful for local dev / unit tests where a DB isn't worth the round trip. Production must set it. ## Schema The migration runs idempotently on startup. The columns track: * `broadcast_id` — primary key (uuid v4). * `tenant_id` — same as `user_id` today; kept distinct so the multi-tenant rewrite doesn't need another migration. * `user_id` — owner of the broadcast. * `status` — one of `live` / `recovering` / `stopped` / `degraded` / `failed`. * `started_at` — start time (UTC). * `last_heartbeat_at` — bumped by the watchdog on every poll that finds the containers still healthy. Used by the startup recovery path to decide whether to attempt respawn (stale = `now() - 5min`). * `restart_count` — bumped each time the watchdog respawns one of the containers; capped at 10 / hour after which `degraded` is set. * `broadcaster_container`/`chat_bridge_container` — current container ids the watchdog watches. * `preset_id` — passed through to spawned containers. * `scene_url` — what the broadcaster Chromium opens. * `rtmp_target` — current RTMP egress (optional; control plane doesn't pick the target today, but the column is reserved so the multi-tenant rewrite doesn't need another migration). * `agent_id` — primary agent for this broadcast (today always `user_id` per the single-agent model; reserved for future). * `room_id` — optional lounge room id when the broadcast is wired through a multi- agent lounge.

live-control/resilience

3 features
  • live-control.resilience.startup-recovery
    live-control/src/recovery/startup.rs:3
    Before the HTTP server binds, [`recover_on_startup`] scans Postgres for broadcasts with status live/recovering/degraded and a stale heartbeat (> 5 min). Containers still running are re-attached; gone containers are respawned. This is the load-bearing path for the 24 h uptime guarantee — without it, an OOMkill of the control plane would orphan every active broadcaster. ## Startup recovery On boot, before serving the first HTTP request, the recovery task asks the store for every row whose status is still `live` and whose `last_heartbeat_at < now() - 5min`. Each of those rows is what the watchdog from the previous lifetime was supposed to be tending; the process died before it could tick again. For each one we: * Inspect the recorded container ids. If both are still running, just rebuild the in-memory `ActiveBroadcast` entry and resume watching (the new watchdog will heartbeat the row within 15 s). * If either container is gone or exited, spawn new ones with the original preset + scene_url + secret, increment restart_count, transition the status `live` → `recovering` → `live` (or `failed` if respawn busts). This is the load-bearing path for the 24 h server uptime guarantee — without it, a single OOMkill of the control plane orphans every active broadcaster + chat-bridge with no supervision.
  • live-control.resilience.watchdog
    live-control/src/recovery/mod.rs:20
    See [`watchdog`] for the every-15s container respawn loop and the restart-budget / Hub `broadcast.degraded` event contract. # @feature live-control.resilience.startup-recovery See [`startup`] for the boot-time hydrate + reattach + respawn path that holds the 24 h uptime guarantee across control-plane restarts.
  • live-control.resilience.watchdog
    live-control/src/recovery/watchdog.rs:3
    Every 15 s (LIVE_CONTROL_WATCHDOG_INTERVAL_SECS) the watchdog inspects each active broadcast's container pair. Crashed containers are respawned up to MAX_RESTARTS_PER_HOUR (10). After the budget is exhausted the broadcast is flipped to `degraded` and a broadcast.degraded event is POSTed to auth's /internal/hub-event so the Hub UI surfaces the failure. ## Watchdog loop Every 15 s (configurable via `LIVE_CONTROL_WATCHDOG_INTERVAL_SECS`) we walk every broadcast in the in-memory `active` map and `docker inspect` both the broadcaster and chat-bridge containers. If either reports `state.status = "exited"` and the broadcast is still flagged `live` in the store, we respawn the container with the same env+labels and bump `restart_count`. If the broadcast has already burned 10 restarts in the last hour we flip it to `degraded` and emit a Hub event instead of looping forever.

live/agent-desktop

1 feature
  • live.os.standalone-desktop
    live-web/src/app/os/[user]/page.tsx:29
    Standalone Agent Desktop OS — fullscreen virtual desktop the agent drives via the same scene-control WS as the OBS scene mode. Persists window layout in localStorage so the surface survives reloads. Foundational v0 of the "fake OS in the browser" track.
    page:/os/[user
    since 0.4.0

live/api

3 features
  • live.api.browse-session
    live-web/src/app/api/browse-session/route.ts:4
    Server-side proxy that mints openalice-browse sessions for the OS BrowserApp. Keeps the internal secret on the server.
    api:POST /api/browse-sessionapi:DELETE /api/browse-session
    since 0.2.0
  • live.api.health
    live-web/src/app/api/health/route.ts:4
    Liveness probe — returns 200 with timestamp for monitoring + Traefik healthchecks.
    api:GET /api/health
    since 0.1.0
  • live.api.pty-session
    live-web/src/app/api/pty-session/route.ts:4
    Server-side proxy that mints openalice-pty sessions for the OS TerminalApp. Keeps the internal secret on the server and hands the browser a fully-formed ws_url with a one-shot token query param.
    api:POST /api/pty-sessionapi:DELETE /api/pty-session
    since 0.3.0

live/audio

6 features
  • live.scene.bgm-player
    live-web/src/app/scene/[user]/lib/BgmPlayer.tsx:99
    Per-stream background-music engine. Hidden <audio> element loops the streamer's chosen BGM track (preset OR custom upload). Drives volume / loop / fade-in / fade-out / cross-fade-on-track-change / auto-ducking-when-TTS-speaks. Decoupled from voice + SFX surfaces; ducking listens to oa-voice-chunk + oa-voice-finished postMessages.
    component:BgmPlayer
    since 0.10.0
  • live.scene.bgm.presets
    live-web/src/app/scene/[user]/theme/bgmPresets.ts:29
    Bundled background-music preset library — five royalty-free vibes (lofi-amber, synthwave-cyan, jazz-cafe, ambient-spacy, silence). Hub picker uses these as starting points; tenant-uploaded tracks plug into the same dropdown via the persona /v1/me/audio CRUD.
    since 0.10.0
  • live.scene.bgm-rotator
    live-web/src/app/scene/[user]/lib/BgmRotator.tsx:42
    Wires the bgmRotation policy + persona-server SSE rotation events into the live scene BGM pipeline. Subscribes to idle.bgm_change, falls back to a client-side max-loops trigger, and applies a 2s crossfade via the underlying BgmPlayer.
    component:BgmRotator
    since 0.12.0
  • live.scene.radio-player
    live-web/src/app/scene/[user]/lib/RadioPlayer.tsx:48
    Live-page radio channel subscriber. Resolves a radio:<slug> bgm_source into a WebSocket subscription against openalice-radio; each now_playing frame updates the underlying BgmPlayer config so the live page picks up new tracks with the same cross-fade / duck-during-tts plumbing as static BGM.
    component:RadioPlayer
    since 0.11.0
  • live.scene.sfx-engine
    live-web/src/app/scene/[user]/lib/SfxPlayer.tsx:63
    Sound-effects audio engine for the live stream player. Listens to chat append / mood transition / gesture / subscribe / caption events and plays a short clip per channel, debounced per event type. Master / per-event volume + per-event URL overrides via `branding.theme.sfx`. Mounts only when `theme.sfx.enabled === true` so SFX-disabled streams pay zero cost.
    component:SfxPlayer
    since 0.9.0
  • live.scene.sfx.presets
    live-web/src/app/scene/[user]/theme/sfxPresets.ts:23
    Five bundled sound-effect preset vibes (silent / subtle-warm / arcade / sci-fi / nature) — each a coordinated set of audio clips for the stream player's chat, mood, gesture, subscribe, and caption events. Hub picker uses these as starting points; per-event URL overrides land on top via `branding.theme.sfx.sfx_<event>_url`.
    since 0.9.0

live/auth

7 features
  • live.auth.forgot-password
    live-web/src/app/forgot-password/page.tsx:22
    Public form that asks tenants to email a password-reset link. Always returns generic copy to avoid account-enumeration leaks.
    page:/forgot-password
    since 0.3.0
  • live.auth.login
    live-web/src/app/login/page.tsx:37
    Public login form — email + password, two-leg MFA prompt for accounts with TOTP/email-MFA.
    page:/login
    since 0.3.0
  • live.auth.password-strength
    live-web/src/components/AuthShell.tsx:11
    Heuristic password-strength meter (close-enough to zxcvbn) plus visual bars. Stays local until v0.7 promotes a shared meter.
    component:PasswordStrengthMeter
    since 0.3.0
  • live.auth.reset-password
    live-web/src/app/auth/reset-password/page.tsx:24
    Lands here from the password-reset email link. Form: new password + confirm. POSTs to /tenants/reset-password.
    page:/auth/reset-password
    since 0.3.0
  • live.auth.session-bridge
    live-web/src/lib/auth-session.ts:19
    Stash tenants-issued JWT into the shared SSO localStorage slot.
    module:src/lib/auth-session
    since 0.3.0
  • live.auth.signup
    live-web/src/app/signup/page.tsx:39
    Public signup form — email, username, display name, password (with strength meter), ToS opt-in.
    page:/signup
    since 0.3.0
  • live.auth.verify-email
    live-web/src/app/auth/verify-email/page.tsx:23
    Confirms the verification token from the welcome email. Auto-POSTs on mount; surfaces a re-send button on failure when the user is logged in.
    page:/auth/verify-email
    since 0.3.0

live/iframe-bridge

1 feature
  • live.persona-iframe.agent-target-param
    live-web/src/app/scene/[user]/modes/personaIframeUrl.ts:312
    Encode `move_target` / `teleport_target` Vec3+meta as base64url URL params so live → persona iframe passes agent walk / teleport intents on mount, no postMessage round-trip needed.
    function:encodeAgentTargetParam
    since 0.4.0

live/loading

1 feature
  • live.scene.global-preloader
    live-web/src/app/scene/[user]/lib/AssetPreloader.tsx:65
    Game-style up-front asset preloader — fetches every URL the active theme references (VRM via persona, HDR env, BGM, scene background video/image, parallax layers, custom fonts, SFX bundle) before the scene fades in. Branded progress UI with X/Y count and percentage. 30 s safety timeout, soft per-asset failures don't block the scene. Eliminates the late-load "model loads on first scene with avatar" hiccup.
    since 0.11.0

live/marketing

1 feature
  • live.landing.hero
    live-web/src/app/page.tsx:14
    Marketing landing for OpenAlice Live — stream with Alice as AI co-host.
    page:/
    since 0.1.0

live/os

2 features
  • live.os.terminal-app
    live-web/src/app/os/[user]/apps/TerminalApp.tsx:7
    Real xterm.js terminal in the Agent Desktop OS — connects to openalice-pty over WebSocket and renders any TUI (claude, vim, btop, htop, neovim, tmux) pixel-perfect with full ANSI escape support.
    since 0.3.0
  • live.os.window-frame
    live-web/src/app/os/[user]/WindowFrame.tsx:19
    One floating window in the virtual desktop scene — owns drag (titlebar) + resize (bottom-right grip) gesture state via raw pointer events, commits final coords to the page-level reducer so layouts persist across reloads.

live/os/apps

2 features
  • live.os.browser-app
    live-web/src/app/os/[user]/apps/BrowserApp.tsx:18
    In-scene browser window backed by openalice-browse — JPEG-stream WS rendered to an img element, pointer/keyboard events mirrored back as command frames, hostname allowlist defence so the agent can only navigate to approved sites.
  • live.os.files-app
    live-web/src/app/os/[user]/apps/FilesApp.tsx:18
    Agent-driven file browser in the Agent Desktop OS — renders the directory listing the agent pushes over the scene-control WS (files frame → SceneEvent::Files { path, entries }). No static stub; an explicit empty-state until the agent walks a directory.
    since 0.4.0

live/scene/lib

1 feature
  • live.scene.global-preloader
    live-web/src/app/scene/[user]/lib/GlobalPreloader.tsx:13
    Branded "now loading" overlay paired with AssetPreloader — Atelier gradient mesh, OA brand mark, progress bar with "X of Y · NN%" count, reduced-motion fallback; uses system fonts so it works before custom fonts load.

live/scene/modes

3 features
  • live.scene.desktop-os
    live-web/src/app/scene/[user]/modes/desktop-os.tsx:17
    Virtual desktop scene mode — macOS/Windows-themed wallpaper + dock + menu bar with agent avatar floating in a corner via the shared persona iframe; reads osDesign from URL `?os=<base64-json>`.
  • live.scene.persona-canvas-host
    live-web/src/app/scene/[user]/modes/PersonaCanvasHost.tsx:12
    Single long-lived persona iframe shared across all VRM scenes — scenes register PersonaCanvasConfig and the host animates transform/opacity/filter via CSS transitions, posting `oa-scene-layout` so the persona side reconfigures without reloading the iframe.
  • live.scene.splitscreen-2
    live-web/src/app/scene/[user]/modes/splitscreen-2.tsx:22
    Two-cell guest-panel scene mode — homogeneous broadcasts collapse into a single persona iframe (`?layout=split-2`) for 3× GPU savings; heterogeneous broadcasts fall back to per-cell iframes with independent SFU audio + scene-bus subscriptions.

live/scene/theme

2 features
  • live.scene.theme-presets
    live-web/src/app/scene/[user]/theme/presets.ts:16
    Catalogue of streamer-segment theme presets — fully-populated StreamTheme objects (colour / typography / texture / corner radius / shadow / motion) used as starting points for branding.theme overrides.
  • live.scene.theme-resolver
    live-web/src/app/scene/[user]/theme/resolveTheme.ts:13
    Pure preset-lookup + override-merge that turns a streamer's branding.theme into a fully-populated StreamTheme, then flattens it into a `--stream-*` CSS-variable map for the scene shell.

live/scenes

17 features
  • live.scene.agent-overlay
    live-web/src/app/scene/[user]/lib/SceneOverlay.tsx:4
    Agent-driven on-stream overlay renderer (lower-third / card / slides / image / product). Self-contained like ModerationOverlay — listens to the `oa-overlay` window CustomEvent (dispatched by the scene-control `overlay` message handler) and paints the active overlay, no props/state threading through the scene page. Drives off the `show_overlay`/`clear_overlay` agent-protocol tools once the live-control dispatcher (separately gated) is wired. Content is purely client-rendered; styling uses Atelier stream vars; z-70 (above captions z-60, below alerts z-1000); single active overlay; ttl auto-hide or explicit clear; animations are CSS transitions (reduced-motion safe).
  • live.scene.agent-panel
    live-web/src/app/scene/[user]/modes/agent-panel.tsx:4
    Multi-agent A2A "talk-show / panel" scene — 2–4 agent guests, each its own persona iframe (broadcast/avatar/label via the shared SplitscreenSlot schema), arranged as a produced panel with TV-chyron name plates, an ON-AIR ring + speaking pulse on the active speaker (driven by SceneContext.focusSlot), and a bottom A2A conversation-thread strip that surfaces recent inter-agent chatter (SceneContext.chatMessages — where the multi-agent dispatcher relays message_agent / hand_off as author=agent frames). The visual home for the agent-protocol message_agent + hand_off tools (the dispatcher relay is the gated server follow-up; this is the client half). Per-slot audio focus reuses the splitscreen oa-mute/oa-unmute postMessage contract. Heavy-render (N persona iframes); needsVoice false (each iframe owns its SFU audio).
    since 0.2.0
  • live.scene.be-right-back
    live-web/src/app/scene/[user]/modes/be-right-back.tsx:25
    AFK placard with a tasteful sleeping-moon SVG animation, breathing BRB title, and desaturated gradient mesh that signals "paused". Atelier-only, prefers-reduced-motion respected.
    since 0.7.0
  • live.scene.event-celebration
    live-web/src/app/scene/[user]/lib/EventCelebrationOverlay.tsx:80
    Subscribe / follow / donation celebration-overlay layer for the live broadcast page. Listens for `oa-event-celebrate` window events and slides a branded animated card into the scene with platform-coloured chrome, a confetti burst, optional TTS read-out, and per-platform SFX. Queues up to 16 events; honours reduced-motion. Mount-time gated by `theme.event_celebration.enabled`. Closes the polish gap to Streamlabs / StreamElements / Neuro-sama-grade event handling.
    component:EventCelebrationOverlay
    since 0.10.0
  • live.scene.game-overlay
    live-web/src/app/scene/[user]/modes/game-overlay.tsx:4
    Game-mode broadcast overlay — a transparent scene the streamer's OBS Browser Source layers OVER their own game capture (osu! / Minecraft / Project Zomboid / anything). Renders ONLY a frontal facecam avatar pip in a configurable corner (framed with an accent ring + name tag, both positioned from the persona-canvas pip rect so they track the avatar exactly) plus a thin HUD cluster (LIVE pill + game badge) anchored on the half opposite the pip so it never overlaps. Game title comes from branding.game (falls back to branding.title); accent from branding.accent. The page returns a transparent <main> for this scene (see resolveMainBackground) so the game capture below shows through. Config-driven — one scene covers every game via branding instead of per-game forks.
    since 0.2.0
  • live.scene.intermission
    live-web/src/app/scene/[user]/modes/intermission.tsx:31
    Editorial chapter card for between-segments transitions ("1 / 3 — Coding session"). Twin-band gradient mesh, oversized chapter index, slow horizontal wipe transition. Calm, intentional, premium.
    since 0.7.0
  • live.scene.moderation-overlay
    live-web/src/app/scene/[user]/lib/ModerationOverlay.tsx:55
    Administrative toast for AI-driven mod actions on the live scene. Listens for `oa-mod-action` window events and paints a muted lower-left card with an inline Undo button. Separate from the celebratory overlays — visually neutral on purpose.
    component:ModerationOverlay
    since 0.13.0
  • live.scene.mode-registry
    live-web/src/app/scene/[user]/modes/index.ts:8
    Catalog of Live broadcast scene modes — VRM fullscreen + static placards (starting-soon / be-right-back / outro). Each scene declares its voice + render cost so the page knows whether to attach the SFU and the admin can warn on GPU-less hosts.
    since 0.2.0
  • live.scene.music-stage
    live-web/src/app/scene/[user]/modes/music-stage.tsx:4
    Music-mode broadcast scene — 50/50 vertical split with the VRM avatar reacting on the RIGHT half and a now-playing music visualizer on the LEFT (spinning album disc, track title from branding.bgm_track, animated equalizer spectrum). Built for music sets / openalice-radio + Lyria BGM streams (the page's hidden <audio>/RadioPlayer drives the actual sound; this scene paints the visualizer). Reuses the proven buildSideHalfCanvasConfig avatar placement so the persona iframe and the scene chrome never overlap (no z-index fight). Equalizer is CSS-keyframe driven (deterministic per-bar delay/duration, reduced-motion safe), so the music chrome is CPU-cheap — only the avatar half is heavyRender. Themed via the stream-theme CSS vars.
    since 0.2.0
  • live.scene.outro
    live-web/src/app/scene/[user]/modes/outro.tsx:31
    Polished farewell placard with social-handle pills, optional next-stream timestamp, Solar Amber Subscribe CTA, and Plasma Cyan gradient mesh distinct from the warm starting-soon family.
    since 0.7.0
  • live.scene.overlay-types
    live-web/src/app/scene/[user]/lib/overlayTypes.ts:2
    Client-safe types for agent-driven scene overlays. Mirrors the openalice-agent-protocol `show_overlay` tool input (kind/title/subtitle/media_url/data/anchor/ttl_ms/overlay_id) so the live-control dispatcher (gated) can forward a `show_overlay` ToolCall onto the scene-control WS as an `overlay` message verbatim. No server logic, no secrets — purely renderable.
  • live.scene.placard-atelier
    live-web/src/app/scene/[user]/modes/placard-shared.tsx:23
    Shared placard primitives — Atelier-themed gradient mesh, brand watermark, social pills, BGM "now playing" pill, countdown clock. Used by starting-soon / be-right-back / outro / intermission.
    since 0.7.0
  • live.scene.poll-overlay
    live-web/src/app/scene/[user]/lib/PollOverlay.tsx:26
    Engagement poll overlay for the live scene page. Listens for oa-poll-opened / oa-poll-vote / oa-poll-closed window events and paints a Q+A card with growing option bars + linear countdown + winning-option highlight on close. Optional per-platform colour theme.
    component:PollOverlay
    since 0.11.0
  • live.scene.redemption-overlay
    live-web/src/app/scene/[user]/lib/RedemptionOverlay.tsx:58
    Channel-point / SuperChat redemption toast for the live scene page. Listens for `oa-redemption-triggered` window events and paints a per-platform branded card ("🎁 ViewerName redeemed «Evil Laugh»"). Separate from the EventCelebrationOverlay (which handles sub/follow/cheer); this one is the dedicated viewer-trigger surface.
    component:RedemptionOverlay
    since 0.11.0
  • live.scene.scene-background
    live-web/src/app/scene/[user]/modes/SceneBackgroundLayer.tsx:184
    Animated 2D backdrop renderer for vtuber scenes — color / image / video / preset modes plus parallax layers, atmospheric particles (snow / cherry-blossoms / embers / dust / rain / stars), and drift (slow-pan / Ken-Burns). Pure CSS keyframes, no canvas, no WebGL — runs at ~0% GPU on the no-GPU broadcaster host. Reduced-motion strips every animation.
    since 0.9.0
  • live.scene.spatial
    live-web/src/app/scene/[user]/modes/spatial.tsx:4
    Spatial / Gaussian-splat broadcast scene — renders a 3D Gaussian-splat environment (a scanned room, a captured location, a generated set) as the full-screen background with the agent's VRM avatar composited in front, so the agent appears to stand *inside* a photoreal 3D space. The splat is loaded from `branding.splat_url` (any .splat / .ply / .ksplat — Luma / Polycam / a scan / a gen service) and a slow auto-orbit gives parallax; `branding.splat_autorotate=false` holds a fixed framing. Clean instructional empty-state when no URL is set. Live splat *generation* (feed a prompt/scan to a gen service that returns the URL) is the server-side follow-up; this scene is the render surface + agent compositing. Heavy (three.js + drei) so it's dynamically imported (code-split, ssr:false) by the registry.
    since 0.2.0
  • live.scene.starting-soon
    live-web/src/app/scene/[user]/modes/starting-soon.tsx:29
    Polished pre-roll placard with Atelier gradient mesh, animated countdown to start_at_ms, brand block, and "now playing" pill. Atelier-only colours, calm motion, prefers-reduced-motion respected.
    since 0.7.0

live/showcase

6 features
  • live.showcase.catalogue
    live-web/src/app/showcase/_data/scenes-catalogue.ts:11
    Scene catalogue metadata for the /showcase index — layout-schema wireframes, category buckets, launch-param presets for every scene + sub-variant.
    page:/showcase
    since 0.4.0
  • live.showcase.filter
    live-web/src/app/showcase/_components/ShowcaseFilter.tsx:25
    Client-side filter + grid for /showcase — single-select category chips + deferred substring search across labels / descriptions / tags / scene IDs.
    component:ShowcaseFilter
    since 0.4.0
  • live.showcase.index
    live-web/src/app/showcase/page.tsx:19
    /showcase — full catalogue of Live scene modes + sub-variants. Wireframe schema thumbnails, editorial specimen layout, deep-link launchers with picker / slot params. Single source of truth for "what scenes does Live have".
    page:/showcase
    since 0.4.0
  • live.showcase.opengraph-image
    live-web/src/app/showcase/opengraph-image.tsx:17
    1200×630 generated OG card for /showcase — dark observatory composition with scene-count cardinal and category chip strip. Used by link unfurls.
    route:/showcase/opengraph-image
    since 0.4.2
  • live.showcase.scene-card
    live-web/src/app/showcase/_components/SceneShowcaseCard.tsx:28
    One card on /showcase — wireframe schema thumbnail + scene metadata + launch-preset buttons. Built on @openalicelabs/ui Card + Badge + Button primitives.
    component:SceneShowcaseCard
    since 0.4.0
  • live.showcase.schema-thumbnail
    live-web/src/app/showcase/_components/SchemaThumbnail.tsx:13
    Pure-SVG wireframe diagram of a scene's layout shape, colour-coded by role (vrm / browser / cli / chat / placard / ide). Renders as a card thumbnail in /showcase.
    component:SchemaThumbnail
    since 0.4.0

live/streaming

4 features
  • live.scene.demo-picker
    live-web/src/app/scene/[user]/_components/DemoPicker.tsx:37
    Floating reviewer-navigation overlay on the live demo page (?demo=1&picker=1) — six tabs (Scene / Render / Avatar / Theme / Background / Audio) let a reviewer hop between any combination of scene modes, avatar render modes/presets, theme presets, background presets, BGM and SFX presets in two clicks each. Pauses the auto-cycling demo timeline so selections stick. URL state syncs via history.replaceState so refreshes keep the reviewer's working selection.
    since 0.11.0
  • live.scene.demo-timeline
    live-web/src/app/scene/[user]/demo-timeline.ts:71
    Canned timeline of scene/mood/chat/gesture/window/splitscreen events the demo room replays on a loop so we exercise every renderer mode without requiring live agents or a WS connection.
    since 0.6.0
  • live.scene.error-boundary
    live-web/src/app/scene/[user]/error.tsx:6
    Route-segment ErrorBoundary for /scene/[user]. Any unhandled error in the OBS Browser Source page renders a "Reconnecting…" card on a dark background instead of a blank surface, then auto-reloads after 3s so transient failures (WS drop, iframe race, persona crash) recover without operator intervention.
    route:/scene/[user
    since 0.4.3
  • live.scene.obs-source
    live-web/src/app/scene/[user]/page.tsx:68
    OBS Browser Source endpoint — multi-mode renderer (VRM fullscreen + static placards) driven by ?scene= query param and a live-control WebSocket so the agent's ChangeScene ToolCall flips scenes mid-stream without restarting the broadcaster Chromium.
    page:/scene/[user
    since 0.2.0

live/studio

7 features
  • live.studio.agents.marketplace
    live-web/src/app/studio/agents/marketplace/page.tsx:48
    Agent Marketplace browse + pair UI — capability-scoped grants against openalice-tenants.
    page:/studio/agents/marketplace
    since 0.2.0
  • live.studio.audio-mixer
    live-web/src/app/studio/audio/page.tsx:10
    Per-channel volume mixer + BGM picker for the active broadcast. Sliders for mic / desktop / TTS / BGM (linear UI, log-mapped on send), default royalty-free BGM presets, custom URL field, play/pause/skip transport, persisted per-broadcast in localStorage, debounced 200 ms before POST to live-control.
    page:/studio/audio
    since 0.3.18
  • live.studio.chat
    live-web/src/app/studio/chat/page.tsx:25
    Multi-platform live chat reader + reply box for the streamer. Subscribes to the live-control scene WS for inbound, posts to chat-bridge for outbound. Lets the streamer talk to viewers without leaving studio.
    page:/studio/chat
    since 1.1.0
  • live.studio.dashboard
    live-web/src/app/studio/page.tsx:113
    Streamer-facing studio — shows the OBS browser-source URL with copy-to-clipboard. Login required.
    page:/studio
    since 0.1.0
  • live.studio.onboarding
    live-web/src/app/studio/onboarding/page.tsx:21
    First-run checklist for Live — avatar, voice, stream destination, desktop pairing, branding. Each step deeplinks to the editor that fixes it.
    page:/studio/onboarding
    since 1.0.0
  • live.studio.settings
    live-web/src/app/studio/settings/page.tsx:37
    Per-user TTS / STT / avatar / system-prompt editor for the Live product. Saves to agent_config; takes effect on the next /live/start.
    page:/studio/settings
    since 1.0.0
  • live.studio.settings.privacy
    live-web/src/app/studio/settings/privacy/page.tsx:60
    Privacy & Data settings — consent flags, GDPR export, audit log, paired agents, account deletion.
    page:/studio/settings/privacy
    since 0.2.0

live/tenants

1 feature
  • live.tenants.client
    live-web/src/lib/tenants-client.ts:14
    Browser HTTP client for openalice-tenants — consent flags, audit log, GDPR export, account deletion, paired-agent grants.
    module:src/lib/tenants-client
    since 0.2.0

live/theming

1 feature
  • live.scene.stream-theme
    live-web/src/app/scene/[user]/theme/types.ts:23
    Per-stream visual theme for the broadcast player. Decouples streamer-facing scene look from the Atelier platform tokens — themes are CSS variables (bg / fg / accent / fonts / texture / motion) scoped to the player's <main> element. Streamers pick from a preset library (vtuber-warm, vtuber-neon-cyan, minimal-mono, glass-dark, vintage-sepia, pastel-bubblegum, editorial-print) or pass a fully-custom override. Atelier values are the fallback so existing broadcasts paint identically.
    since 0.8.0

stream-host/audio

2 features
  • stream-host.audio.process-loopback-capture
    live-stream-host/src/wasapi_loopback.rs:89
    ProcessLoopbackCapture — Windows-only RAII guard. Calls ActivateAudioInterfaceAsync against `VAD\\Process_Loopback` with PROCESS_TREE mode + the daemon's PID, opens an IAudioClient at 48k/2ch/Float32, pumps an IAudioCaptureClient → user-supplied sink in an event-driven loop. Mic + system sounds physically excluded by the kernel API. Any activation error degrades to silent-PCM fallback so ffmpeg never starves; OA_DISABLE_AUDIO=1 short-circuits to fallback.
    since 0.3.18
  • stream-host.audio.wasapi-loopback
    live-stream-host/src/wasapi_loopback.rs:1
    Per-process WASAPI loopback capture on Windows — uses ActivateAudioInterfaceAsync with VAD\\Process_Loopback + PROCESS_TREE mode to grab only the daemon + WebView2 child audio (scene <audio>, TTS) while physically excluding mic / system sounds; falls back to silent PCM on any activation error so ffmpeg never starves.
    since 0.3.18

stream-host/capture

6 features
  • stream-host.capture.cross-platform-process
    live-stream-host/src/capture.rs:180
    CaptureProcess — RAII wrapper around the OS-native ffmpeg capture child (gdigrab on Windows, x11grab on Linux, avfoundation on macOS). Owns the per-destination stderr scanner thread classifying RTMP failures. Drop / stop sends "q" + SIGTERM to flush the RTMP trailer so the platform's slot releases immediately.
    since 0.2.0
  • stream-host.capture.encoder-chain
    live-stream-host/src/capture.rs:1
    Hardware encoder auto-detection at startup: `ffmpeg -encoders` is probed once and the first available encoder wins: h264_nvenc → h264_qsv → h264_amf → h264_videotoolbox → h264_vaapi → libx264 (software fallback). Pin a specific encoder with OA_VIDEO_ENCODER env var. Capture + RTMP push pipeline. We shell out to a system `ffmpeg` rather than statically linking libavcodec — the binary is universal across platforms, ships with every major package manager, and avoids dragging a 50 MB GPL/LGPL bundle into our installer (Tauri Mac/Win signing gets unhappy with that). Flow: capture device (x11grab / gdigrab / avfoundation) → libx264 (balanced preset, 4 Mbps default) → flv muxer → RTMP tee to N destinations Hardware encoders (NVENC / QuickSync / VideoToolbox / VAAPI) land in v0.3 once we have a stable baseline to compare against. For v0.2 the soft-encoder is universal.
  • stream-host.capture.platform-backends
    live-stream-host/src/wgc.rs:1
    Per-OS capture backends: Windows.Graphics.Capture + WASAPI process-loopback (Windows); avfoundation (macOS); x11grab + PulseAudio (Linux). WGC uses a fixed-size frame pool + bounded ring-buffer so steady-state allocation is zero at 1080p60. Windows.Graphics.Capture (WGC) — per-HWND screen capture. WGC is the modern Win10+ API that OBS uses for "Window Capture (Auto)" mode. It reads the *post-composition* pixels of a specific HWND from the DWM compositor, so: * DirectComposition / DXGI / hardware-accelerated surfaces (WebView2's actual content) are captured correctly. * Privacy is iron-clad — only the requested window's pixels ever leave the GPU; desktop / other apps never enter the capture pipeline. * The capture follows the window across monitors / resizes / minimize→restore without re-arming. This module: 1. Finds the OpenAlice kiosk window by its title (the same title gdigrab title-mode used to look up). 2. Spawns a WGC capture session on that HWND. 3. Pipes raw BGRA frames into a child ffmpeg process's stdin at the configured framerate. 4. Hands ownership of the ffmpeg child + the WGC handle back to the daemon so it can stop both on broadcast end. ## Backpressure (v0.3.20 — full ring-buffer + frame pool) The frame pipeline is **WGC thread → bounded ring buffer → writer thread → ffmpeg stdin**, with every BGRA buffer drawn from a **fixed-size frame pool**. v0.3.19 already moved the direct ffmpeg-stdin write off the WGC callback (which fixed the freeze-when-RTMP-stalls audit finding); this slice finishes the job: * **Frame pool** — at 1080p60 the per-frame `Vec<u8>::to_vec` in v0.3.19 churned ~500 MB/s of allocator traffic. The pool pre-allocates `POOL_CAP = QUEUE_CAP + POOL_HEADROOM` BGRA buffers up front, hands them out by checkout, and recycles them when the writer finishes a frame. Steady-state allocation drops to zero. * **Oldest-drop policy** — when the ring buffer is full we evict the *oldest* pending frame and enqueue the new one. v0.3.19 dropped the *newest*, which made congestion show as a stuck stream catching up to current time slowly. The new frame is more useful to the viewer; the older one is already stale. * **Adaptive bitrate hint** — the writer thread tracks a trailing 10 s drop ratio and emits a `WARN bitrate too high for upload — drop X.Y%` line when the rate exceeds ~1 frame/sec. We don't actually restart ffmpeg with a new bitrate (the operator controls that); we just surface the diagnostic so the bottleneck is obvious. * **Per-destination tee-failure detection** — the ffmpeg stderr scanner from `capture::spawn_ffmpeg_stderr_scanner` parses `Connection failed` / `RTMP_Connect` / `Broken pipe` lines and tags them with the destination name, so "Twitch failed but YT still live" is visible instead of the silent `onfail=ignore` swallow. Operator-facing log lines on a healthy 60 fps broadcast: ```text WGC pipe stats: enqueued=300 written=300 dropped=0 hwm=1/4 ``` Under congestion: ```text WGC pipe stats: enqueued=600 written=540 dropped=60 hwm=4/4 WARN bitrate too high for upload — drop 10.0% WARN ffmpeg RTMP destination failure dest=Twitch line=... ```
  • stream-host.capture.start-stream
    live-stream-host/src/capture.rs:251
    Boots the cross-platform capture+encode+push child. Auto-detects hardware H.264 encoder (NVENC → QSV → AMF → VideoToolbox → VAAPI → libx264) via `ffmpeg -encoders`, builds the OS-specific capture device args, configures the tee muxer to fan one encoded stream to N RTMP destinations (no double encoding), tags +genpts so YouTube's PTS sanity-check doesn't park the broadcast.
    since 0.2.0
  • stream-host.capture.supervised-capture
    live-stream-host/src/capture.rs:790
    SupervisedCapture — owns a background supervisor thread running `watchdog::supervise` over `start_stream`. Per-spawn ffmpeg child swap on RTMP drop / crash; exponential 2s→30s backoff, 60s uptime-reset gate; stop() arms the StopSignal + joins the thread.
    since 0.4.0
  • stream-host.capture.wgc-windows
    live-stream-host/src/wgc.rs:149
    WgcCapture — Windows.Graphics.Capture per-HWND capture session. Pipes post-composition BGRA frames through a fixed-size frame pool + bounded oldest-drop ring buffer + dedicated writer thread → child ffmpeg stdin (rawvideo bgra). Owns ffmpeg, capture thread, writer thread, and stderr scanner; Drop arms stop + joins all so the broadcast tears down cleanly and the RTMP slot releases.
    since 0.3.0

stream-host/cli

6 features
  • stream-host.cli.daemon
    live-stream-host/src/daemon/event_loop.rs:60
    `daemon` subcommand — long-running 1-EXE 1-click experience. Kiosk-fullscreen WebView (always-on-top, GPU-compositor disabled on Windows so gdigrab/WGC can read post-composition pixels) polls live-control every 5s for active broadcasts; on Go Live navigates to the scene URL + spawns WgcCapture/start_stream pushing RTMP from the user's GPU. Installs Windows tray icon with Status / Show kiosk / Stop / Update / Quit menu, ctrl-c handler, panic hook, SleepGuard, RSS+disk housekeeping, Hub heartbeat tick, self-update 6h cadence.
    cli:daemon
    since 0.3.0
  • stream-host.cli.doctor
    live-stream-host/src/cli.rs:411
    `doctor` subcommand — runs ffmpeg::ensure_ffmpeg (auto-downloads if absent) + ffmpeg::probe to confirm the binary is invocable; surfaces the version string. One-command path to a ready-to-stream machine.
    cli:doctor
    since 0.2.0
  • stream-host.cli.install-service
    live-stream-host/src/cli.rs:381
    `install-service` subcommand — registers the daemon with the platform service manager (launchd plist KeepAlive=true, systemd user unit Restart=always, Windows SCM with failure-restart 5000ms ×3). Prints the activation step the user runs next.
    cli:install-service
    since 0.4.4
  • stream-host.cli.pair
    live-stream-host/src/cli.rs:198
    `pair <CODE>` subcommand — exchanges Hub's 8-char pairing code at POST {auth_base}/auth/devices/pair/claim for a 90-day device JWT + user_id, writes them into stream-host.toml (merging with any existing config), then walks the user through RTMP setup + quality preset on first pair. Subsequent launches boot straight into stream mode.
    cli:pair
    since 0.2.0
  • stream-host.cli.pick-destinations
    live-stream-host/src/cli.rs:132
    Resolves the RTMP push targets. Empty filter → every primary=true destination; named filter → those entries by name. Errors with clear messages when the config is empty or an --only name doesn't exist; foundation of the multi-platform tee.
    since 0.2.0
  • stream-host.cli.setup
    live-stream-host/src/cli.rs:319
    `setup` subcommand — interactive re-run wizard. Re-prompts RTMP destinations, quality preset, capture region, then offers to install the auto-start service (launchd / systemd / SCM). Requires an existing config (pairing must have happened).
    cli:setup
    since 0.2.0

stream-host/config

5 features
  • stream-host.config.capture-region
    live-stream-host/src/config.rs:318
    CaptureRegion — explicit (x, y, width, height, fps) rectangle for the platform-native capture backend. Overrides the QualityPreset's resolution+fps when set; legacy escape hatch for crop-and-stream workflows.
    since 0.1.0
  • stream-host.config.host-config
    live-stream-host/src/config.rs:40
    HostConfig — serde-deserialized root of `~/.config/openalice/stream-host.toml`. Carries scene_base, paired user+token, optional broadcast/initial_scene, window dims, RTMP destinations vec, optional capture region, quality_preset enum, optional recording leg. CLI flags override per-invocation.
    since 0.4.0
  • stream-host.config.quality-preset
    live-stream-host/src/config.rs:124
    QualityPreset enum — named ladder P480/P720/P1080/P1440/P2160. `params()` returns the (width, height, fps, bitrate) tuple. OA_QUALITY_PRESET env overrides the TOML field, OA_VIDEO_BITRATE overrides the bitrate baseline.
    since 0.4.0
  • stream-host.config.rtmp-destination
    live-stream-host/src/config.rs:235
    RtmpDestination — one named RTMP target (name + url + stream_key + primary flag). The daemon tees one capture to every primary=true destination so YouTube + Twitch get identical feeds with zero re-encoding cost.
    since 0.1.0
  • stream-host.config.toml-cli
    live-stream-host/src/config.rs:1
    TOML config loader (~/.config/openalice/stream-host.toml) merged with CLI flag + env overrides, including the named quality-preset ladder (P480..P2160) with bitrate/fps baselines and explicit [capture] width/height/fps escape hatch; Hub writes this file on device pairing.
    since 0.4.0

stream-host/core

1 feature
  • stream-host.core.webview-renderer
    live-stream-host/src/main.rs:1
    Platform-native WebView (WebView2 / WKWebView / WebKitGTK) opens the OpenAlice scene URL on the streamer's local GPU. The server only sends scene-control commands over WebSocket — no AI inference on the streamer's box. openalice-stream-host — desktop thin-client. Wave 13-A (2026-05-23) split the historic 2347-LOC main.rs into [`cli`] (clap + subcommand runners), [`webview`] (bare WebView + `stream` path), and [`daemon`] (kiosk + RTMP push + tray + heartbeats + housekeeping + self-update). `main()` is now a thin dispatcher that wires the boot sequence and routes to the right module.

stream-host/daemon

2 features
  • stream-host.daemon.sleep-prevention
    live-stream-host/src/sleep_prevention.rs:1
    OS sleep prevention while streaming: SetThreadExecutionState (Windows), IOPMAssertion (macOS), systemd-inhibit (Linux). Display can dim; the broadcast machine never suspends. Prevent the operating system from sleeping the broadcaster's PC mid-stream. ## Why this matters for 24h streams Default OS power profiles on Windows, macOS and most Linux desktops put the box to sleep after 15–30 min of "user inactivity" — keyboard idle. A stream-host running in the tray looks idle to those heuristics; the box goes to sleep, the broadcast ends. We need to tell the OS "I'm doing something important, hold off the sleep timers." Per-platform APIs: * **Windows** — `SetThreadExecutionState` with `ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED`. `ES_AWAYMODE_REQUIRED` is the Vista-era "media center" flag that keeps the system "available" while the display can still sleep. This is exactly what we want — operator can walk away + the screen dims, but the broadcast machine keeps streaming. * **macOS** — `IOPMAssertionCreateWithName` with type `PreventSystemSleep` (keeps SMC awake while letting the display sleep). The companion `PreventUserIdleSystemSleep` covers "the user might idle but the system shouldn't sleep on that account." We hold both while streaming. * **Linux** — `systemd-inhibit` with the `sleep:idle: handle-lid-switch` set. We spawn `systemd-inhibit --what --why "broadcasting" sleep infinity` and hold the resulting child for the lifetime of the broadcast. Tearing the child down releases the inhibitor. Tests inject a [`SleepBlockerBackend`] so the platform-specific calls can be exercised without touching the host's power state. Production code wires the real backends in the Windows/macOS/Linux `cfg`-gated `default_backend()` factories.
  • stream-host.daemon.watchdog-heartbeat
    live-stream-host/src/watchdog.rs:1
    Background daemon polls live-control every 5 s and auto-launches WebView + capture + RTMP push when the operator clicks Go Live. RTMP watchdog supervises ffmpeg with exponential backoff (cap 30 s); heartbeat POSTs every 30 s to Hub so the dashboard shows live host health. RTMP watchdog — keep the broadcast alive across ffmpeg crashes and transient RTMP drops. ## Why this exists Before v0.4.4 the capture pipeline spawned ffmpeg once and let the broadcast die on any of the failure modes that show up in real 24h streams: * **RTMP ingest drops.** YouTube + Twitch routinely kick a long-running stream off one edge ingest and expect the broadcaster to reconnect. ffmpeg surfaces this as a non-zero exit (`Connection refused`, `Broken pipe`, `End of file`) and the prior code just stopped pushing. * **ffmpeg crashes.** A bad keyframe / OOM / dependency-load failure under tee can SIGSEGV ffmpeg. Same story — process exits, no recovery. * **Network blips.** Wi-Fi drops 30 s; the recovery is trivially "spawn ffmpeg again with the same args." YouTube treats reconnects under ~30 s as a continuous stream — the broadcast counter doesn't reset and viewers don't see a "Stream ended" page. We use exponential backoff capped at 30 s so we always sit inside that window. ## Design The watchdog is a supervisor loop that owns the ffmpeg `Child` handle. The capture pipeline produces a "spawn function" that the watchdog can call repeatedly with the same args; on each exit the watchdog inspects the [`ExitClassification`] and decides whether to reconnect. The watchdog is intentionally agnostic about *what* ffmpeg is doing — it doesn't know about RTMP destinations, the recording leg, audio devices, or encoder choices. All it sees is "the child exited; restart it or surrender." Keep it that way. Reconnects emit a [`WatchdogEvent::StreamInterruptionRecovered`] the heartbeat client consumes so the dashboard can surface a reliability indicator ("recovered from 2 ffmpeg crashes today"). ## Why we don't kill recording on RTMP drop The local mp4 writer lives inside the same ffmpeg tee, so on ffmpeg respawn it starts a **new** file. That's deliberate: splitting the recording into 30s-of-blackout-bracketed chunks is preferable to losing the file entirely. The new file name includes a `.partN.mp4` suffix so the user can stitch them post-hoc with `ffmpeg -f concat`. The original v0.4.2 recording leg is unchanged; the watchdog simply asks for the "next part" path on each respawn.

stream-host/disk-monitor

5 features
  • stream-host.disk-monitor.classify
    live-stream-host/src/disk_monitor.rs:117
    Pure classifier — given a free-space sample + write-rate, returns DiskVerdict (Ok / LowSpaceWarning with minutes-until-critical estimate / CriticalStopRecording). Three-tier ladder mirrors recording.rs pre-flight: 10 GB warn, 5 GB critical, 20 GB recovery target.
    since 0.4.0
  • stream-host.disk-monitor.free-gb
    live-stream-host/src/disk_monitor.rs:146
    Cross-platform free-space probe in GiB for a directory path — GetDiskFreeSpaceExW on Windows, statvfs on Unix. Returns None on platform failure so the caller errs toward "keep streaming, log a warn".
    since 0.4.0
  • stream-host.disk-monitor.purge-until-free
    live-stream-host/src/disk_monitor.rs:256
    Deletes oldest-first recordings under `dir/old/` until the partition reaches target_gb free or the directory empties. Returns the deleted paths + bytes reclaimed. Last-resort cleanup path during a long-form broadcast.
    since 0.4.0
  • stream-host.disk-monitor.recording-protection
    live-stream-host/src/disk_monitor.rs:1
    60s disk-space poll during a broadcast — warns the Hub under 10 GB, stops the recording leg under 5 GB, and auto-rotates completed recordings into old/ under 20 GB to free runway; RTMP push never stops regardless ("RTMP wins, recording is sacrificial").
    since 0.4.0
  • stream-host.disk-monitor.rotate-completed
    live-stream-host/src/disk_monitor.rs:208
    Rename-based rotation of completed `*.mp4`/`*.mkv`/`*.flv` recordings into `dir/old/`. Same-filesystem rename = O(1), idempotent, skips conflicts. Triggered when free space drops below RECOVERY_TARGET_GB during a broadcast.
    since 0.4.0

stream-host/ffmpeg

3 features
  • stream-host.ffmpeg.auto-provision
    live-stream-host/src/ffmpeg.rs:1
    Zero-config ffmpeg provisioning — checks PATH, then per-OS user cache (LOCALAPPDATA/openalice or ~/.local/share/openalice), then downloads the official Gyan.dev release-essentials (Windows) or BtbN static build (Linux) and caches it for next time; OA_FFMPEG_PATH overrides for custom builds.
    since 0.2.0
  • stream-host.ffmpeg.ensure
    live-stream-host/src/ffmpeg.rs:35
    Resolves the working ffmpeg binary path. Priority: OA_FFMPEG_PATH env override → PATH lookup → per-OS user cache (LOCALAPPDATA / ~/.local/share) → fresh ~80MB download from Gyan.dev (Windows) or BtbN (Linux) with progress bar. Used by `doctor`, `stream`, and the daemon at boot.
    since 0.2.0
  • stream-host.ffmpeg.probe
    live-stream-host/src/ffmpeg.rs:184
    Probes an ffmpeg binary via `-version`, returning the first reported line. Catches broken downloads / unsupported builds early; surfaced in the `doctor` subcommand output.
    since 0.2.0

stream-host/heartbeat

4 features
  • stream-host.heartbeat.host-id
    live-stream-host/src/heartbeat.rs:208
    Stable host-id loader — reads/creates `<config_dir>/host-id`. Hub dedupes heartbeats across daemon restarts by this id; ephemeral fallback uses a fresh UUIDv4 when the file cannot be created.
    since 0.3.0
  • stream-host.heartbeat.hub-push
    live-stream-host/src/heartbeat.rs:1
    30s heartbeat POST to app.blal.pro/api/internal/streamhost/heartbeat carrying host_id / broadcast_id / RSS / ffmpeg liveness / RTMP state / free disk / watchdog event log / sleep-prevention counters — gated by INTERNAL_BROADCAST_SECRET, best-effort, never crashes the daemon on network blip.
    since 0.3.0
  • stream-host.heartbeat.payload-wire
    live-stream-host/src/heartbeat.rs:163
    HeartbeatPayload — serde wire shape POSTed every 30s to Hub's `/api/internal/streamhost/heartbeat` with X-Internal-Secret. Carries host_id, optional broadcast_id, RSS MB, uptime, ffmpeg_alive flag, last_frame_ts ISO timestamp, RtmpState (Idle/Connected/Reconnecting/Failed), free_disk_gb, interruptions, current+cumulative sleep_blocks, last 10 watchdog events, version.
    since 0.3.0
  • stream-host.heartbeat.send
    live-stream-host/src/heartbeat.rs:288
    POSTs one HeartbeatPayload to Hub's `/api/internal/streamhost/heartbeat` with X-Internal-Secret header. Surfaced under host_id; called every HEARTBEAT_INTERVAL (30s) by the daemon's housekeeping tick. Transport-only — no retry/backoff inside.
    http_client:/api/internal/streamhost/heartbeat
    since 0.3.0

stream-host/pairing

1 feature
  • stream-host.pairing.device-token
    live-stream-host/src/setup.rs:1
    `pair <CODE>` exchanges a Hub-generated 8-char code for a 90-day device-scoped JWT and writes it to ~/.config/openalice/stream-host.toml. Subsequent launches boot straight into stream mode without user interaction. Interactive setup wizard. Two entry points: - `setup` subcommand — full re-run any time (re-prompt pairing code, RTMP destinations, capture region). - `pair` subcommand — after the JWT is claimed, optionally walks the user through adding RTMP destinations so they don't have to crack open the TOML. Goal: zero-config first launch. User runs the .exe, types the pairing code from Hub, optionally adds Twitch/YouTube keys, then `oash stream` Just Works.

stream-host/recording

2 features
  • stream-host.recording.plan-resolver
    live-stream-host/src/recording.rs:65
    RecordingPlan — three-state enum for the VOD leg of the broadcast pipeline. Disabled{reason} (no leg), Local{path, container} (mp4/mkv to disk), Cloud{url} (HTTP POST to recordings.blal.pro). Wraps every leg in `[f=...:onfail=ignore]` so the RTMP push always wins.
    since 0.4.0
  • stream-host.recording.tee-leg
    live-stream-host/src/recording.rs:1
    Optional VOD recording leg added to the ffmpeg tee muxer — captures the same encoded bitstream to local disk OR uploads to recordings.blal.pro/v1/me/recordings/<broadcast_id>; wrapped in onfail=ignore so the RTMP push always wins when disk fills or the cloud endpoint hiccups.
    since 0.4.0

stream-host/rss-monitor

3 features
  • stream-host.rss-monitor.current-rss
    live-stream-host/src/rss_monitor.rs:216
    Cross-platform RSS reader via `sysinfo`. Refreshes the daemon's own PID's memory page and returns bytes-to-MB. Degrades to 0 on platforms where sysinfo can't read process memory; never panics.
    since 0.4.4
  • stream-host.rss-monitor.self-restart
    live-stream-host/src/rss_monitor.rs:1
    Self-monitor that samples daemon RSS every 60s — triggers a graceful service self-restart when RSS exceeds OA_STREAMHOST_MAX_RSS_MB (default 2500 MB) or grows >50% over a 30-min sliding window, so multi-day broadcasts survive WebView2/WGC/tokio leaks without OS pressure.
    since 0.4.4
  • stream-host.rss-monitor.sliding-window
    live-stream-host/src/rss_monitor.rs:120
    RssMonitor — sliding 30-min RSS sampling buffer + classifier. `tick(now, rss_mb)` returns RssVerdict (Ok / GrowthExceeded baseline×growth_ratio / CeilingExceeded). Drives the graceful self-restart on slow leaks (WebView2 / WGC / tokio) so multi-day broadcasts survive without OS pressure.
    since 0.4.4

stream-host/service

1 feature
  • stream-host.service.auto-start
    live-stream-host/src/service_installer.rs:1
    `install-service` registers the daemon as an OS auto-start service: Windows SCM (auto-restart on crash), macOS launchd plist (KeepAlive=true), Linux systemd user unit (Restart=always). Unix variants run in the user domain — no password required. Cross-platform "register as auto-start service" support. Goal: a 24h broadcast survives a crash because the OS itself respawns the daemon within ~5 s. Each platform has its own supervisor: * **Windows** — register under the Service Control Manager (SCM). SCM auto-restarts on crash, runs at boot, and is the native answer to "this thing must always be up." * **macOS** — launchd plist with `KeepAlive = true`. Plist sits in `~/Library/LaunchAgents/dev.openalice.streamhost.plist` and is loaded with `launchctl load -w`. * **Linux** — systemd user unit with `Restart=always`. Sits in `~/.config/systemd/user/openalice-stream-host.service` and is loaded with `systemctl --user daemon-reload && systemctl --user enable openalice-stream-host`. The Windows side requires Administrator rights (SCM is a privileged surface); the Unix sides run entirely in the user domain so the user never has to type a password. This module produces the manifests + drives the install / uninstall flow. The wizard step ("Install as auto-start service?") in `setup.rs` calls into here when the user opts in.

stream-host/service-installer

1 feature
  • stream-host.service-installer.install
    live-stream-host/src/service_installer.rs:155
    Cross-platform auto-start installer. macOS → writes `~/Library/LaunchAgents/dev.openalice.streamhost.plist` (KeepAlive + RunAtLoad), Linux → `~/.config/systemd/user/openalice-stream-host.service` (Restart=always, MemoryMax=4G), Windows → `sc.exe create` + `sc.exe failure` for SCM (auto-restart 5000ms ×3). Returns InstallReport with manifest path + activation steps.
    since 0.4.4

stream-host/setup

5 features
  • stream-host.setup.capture-region
    live-stream-host/src/setup.rs:239
    Optional capture-region prompt — Some(rectangle) for crop-and-stream workflows, None for whole-window default. Surfaced by the `setup` subcommand only; the daemon path always uses the QualityPreset's resolution.
    since 0.2.0
  • stream-host.setup.collect-rtmp
    live-stream-host/src/setup.rs:31
    Interactive RTMP destinations wizard — dialoguer-driven prompts with Twitch / YouTube Live / Kick / Custom presets (pre-filled ingest URLs), redacted display of existing keys, primary-vs-secondary toggle. Drives the first-run `pair` UX + the standalone `setup` subcommand.
    since 0.2.0
  • stream-host.setup.maybe-install-service
    live-stream-host/src/setup.rs:205
    Wizard tail step — offers to install the auto-start service (launchd / systemd / Windows SCM). Default Y so v0.4.4 users get crash-survival + boot-launch; returns Skipped/Installed/Failed so the caller can hint at a manual `install-service` retry path.
    since 0.4.4
  • stream-host.setup.quality-prompt
    live-stream-host/src/setup.rs:154
    Interactive QualityPreset picker. Lists all presets with `WxH @ Nfps, Mk` labels; defaults to the user's current value (P1080 on first run) so re-running setup is one keypress.
    since 0.4.0
  • stream-host.setup.write-config
    live-stream-host/src/setup.rs:122
    Persists a HostConfig as pretty-printed TOML to the path (creates parent dirs as needed). Single canonical writer for all subcommands that mutate `~/.config/openalice/stream-host.toml`.
    since 0.2.0

stream-host/sleep-prevention

2 features
  • stream-host.sleep-prevention.backend-trait
    live-stream-host/src/sleep_prevention.rs:55
    SleepBlockerBackend trait — pluggable per-OS sleep-prevention API. Real backends call SetThreadExecutionState (Windows), IOPMAssertionCreateWithName (macOS), org.freedesktop.login1 Inhibit DBus method (Linux). Returns opaque tokens so multiple legs can stack guards independently.
    since 0.4.4
  • stream-host.sleep-prevention.sleep-guard
    live-stream-host/src/sleep_prevention.rs:145
    SleepGuard — RAII handle. `acquire` takes a sleep-prevention token from the backend; Drop auto-releases. Acquired on broadcast start, dropped on broadcast end / daemon exit / graceful-recycle, ensuring the OS never sleeps mid-stream.
    since 0.4.4

stream-host/updater

4 features
  • stream-host.updater.auto-update
    live-stream-host/src/updater.rs:1
    Self-update via GitHub Releases API checked every 6 h. Downloads the versioned asset, verifies SHA-256 against the sibling .sha256, then hands off via --complete-update chain-launch so the running .exe file-lock releases before the swap. Opt out with OA_AUTO_UPDATE=0. Self-update mechanism for the stream-host daemon. On the daemon's normal poll loop we periodically (5s after boot, then every 6h) hit GitHub's `/releases/latest` endpoint, parse the `tag_name`, and compare it to the running binary's `CARGO_PKG_VERSION`. When a newer release is available we surface it via tracing + (when wired) a tray notification, and offer an install flow. Install flow on Windows: 1. Download the matching .exe asset (named per the release-ops convention `openalice-stream-host-{ver}-x86_64-pc-windows-msvc.exe`) to `%TEMP%\openalice-stream-host-{ver}.exe`. 2. Verify the byte count matches the `Content-Length` header so a truncated download can't run. 3. Verify the SHA-256 of the downloaded body matches the sibling `<asset>.sha256` published next to the .exe in the same release. Mismatch → hard-abort; missing sibling → warn + skip (transitional, will be hard-required in v0.5). 4. Spawn the new exe with `--complete-update <old_path>`. The new exe is responsible for the swap; the OLD exe just exits cleanly so its file lock releases (Windows holds an exclusive lock on a running .exe — you cannot overwrite it from inside). 5. Drop on the running daemon's `WgcCapture` / `CaptureProcess` kills ffmpeg so the platforms see the "stopping" heartbeat before the OS frees the slot. 6. New process on `--complete-update <old>`: sleep 1 s for the old handle to release, copy itself over `<old>`, re-launch the installed-location exe with no special args, exit. Why `--complete-update` chain instead of `MoveFileEx`'s `MOVEFILE_DELAY_UNTIL_REBOOT`? The reboot variant requires admin rights AND only swaps on the next reboot — the user has to restart Windows before they're on the new version. The chain variant works as a normal user, completes in ~2 seconds, and is how electron-builder / Squirrel / Tauri's updater all work. Opt-out: `OA_AUTO_UPDATE=0` skips both the periodic check and the `--complete-update` swap entirely. Useful for sysadmins who pin a specific build.
  • stream-host.updater.check-github
    live-stream-host/src/updater.rs:124
    Polls GitHub `/releases/latest` for OpenAliceLabs/openalice-stream-host. Strict semver newer-than-current comparison + matching Windows asset lookup (`openalice-stream-host-{ver}-x86_64-pc-windows-msvc.exe`). Returns UpdateInfo (version, download_url, size) when a new release is staged.
    since 0.4.0
  • stream-host.updater.complete-update-handoff
    live-stream-host/src/updater.rs:358
    Self-update tail-call handoff. When spawned with `--complete-update <old_exe_path> [--old-pid <pid>]`, sleeps 1s for the old process to release file locks, copies the new exe over the old path, re-launches it, then exits. Sub-microsecond no-op on normal boots. OA_AUTO_UPDATE=0 aborts the swap.
    since 0.4.0
  • stream-host.updater.install-windows
    live-stream-host/src/updater.rs:218
    Windows in-place update — downloads the new exe to %TEMP%, verifies Content-Length + asset size, spawns the new binary with `--complete-update <old_exe_path>` so the new process swaps the file once the old one exits. Returns the new PID so the old daemon can hand off cleanly.
    since 0.4.0

stream-host/watchdog

6 features
  • stream-host.watchdog.backoff-policy
    live-stream-host/src/watchdog.rs:140
    BackoffPolicy — exponential-backoff schedule for ffmpeg respawn. Default initial=2s, max=30s (≤YouTube's reconnect grace so the broadcast counter never resets), reset_after=60s. `at(n)` doubles per consecutive failure capped at max.
    since 0.4.4
  • stream-host.watchdog.classify-exit
    live-stream-host/src/watchdog.rs:318
    Maps (exit_code, signal, stderr_tail, operator_stopped) → ExitClassification. Scans stderr_tail for known RTMP failure patterns (Connection failed/refused/reset, RTMP_Connect, Broken pipe, EOF, I/O error, Failed to update header, Server error) so events surface "Twitch dropped: Connection refused" instead of "exit code 1".
    since 0.4.4
  • stream-host.watchdog.event-log
    live-stream-host/src/watchdog.rs:369
    EventLog — bounded ring-buffer (default 32 entries) of (Instant, WatchdogEvent). Shared between the supervisor loop (writer) + heartbeat tick (reader, flattens last N events into Hub heartbeat payload). Thread-safe via Arc<Mutex<Vec>>.
    since 0.4.4
  • stream-host.watchdog.event-stream
    live-stream-host/src/watchdog.rs:106
    WatchdogEvent — supervisor lifecycle events emitted by `supervise`. Spawned/Exited/BackoffSleep/StreamInterruptionRecovered/OperatorStopAck. Heartbeat client flattens the recent buffer into Hub's reliability dashboard.
    since 0.4.4
  • stream-host.watchdog.exit-classification
    live-stream-host/src/watchdog.rs:68
    ExitClassification — taxonomy of ffmpeg child exits. OperatorStop (no respawn), UnknownExit{code} (respawn, tag for triage), RtmpDropped{hint} (parsed RTMP failure pattern), Signaled{signal} (SIGSEGV/SIGKILL). Drives the supervise() respawn decision.
    since 0.4.4
  • stream-host.watchdog.supervise
    live-stream-host/src/watchdog.rs:247
    Pure supervisor loop — owns the spawn/exit/backoff state machine. Takes injectable spawn + sleep closures so unit tests run without real ffmpeg or wall-clock sleeps. Stops on OperatorStop or armed StopSignal; tags reconnect storm transitions as StreamInterruptionRecovered + bumps the shared InterruptionCounter.
    since 0.4.4