Skip to content

Repository files navigation

CeraUI

Generic raw capture inputs now have an honest, non-streamable Raw video row rather than a false Cam Link identity. Both consumers now pin published bindings 2026.9.11; see publication and rollout.

CI Release

Web-based control interface for live video streaming with cellular bonding. Built with Svelte 5 (frontend) and Bun (backend).

Quick Start

bun install
bun run dev

Opens at http://localhost:6173. Backend runs on port 3002.

Project Structure

CeraUI/
├── apps/
│   ├── frontend/     # Svelte 5 PWA
│   └── backend/      # Bun/TypeScript server
├── packages/
│   ├── rpc/          # Shared RPC schemas + validation constants
│   └── i18n/         # Internationalization (10 languages)
└── docs/             # Documentation

Features

3-Destination Navigation

The app is organized into three primary destinations:

  • Live — a unified device-first source list leads the destination: every capture device, built-in pipeline, test pattern, and LAN network-ingest (RTMP/SRT) slot renders as one picker, with a single "Codec & delay" affordance owning all audio configuration. Below it, a "Stream setup" card shows three always-visible readiness rows (Encoder, Destination, Network — no collapse, no ready bar) each fusing a state dot with its config summary and a one-tap edit/fix affordance, plus the Start control. Pick a source, adjust encoder/server settings, and go live. While streaming, the view switches to a live cockpit: telemetry strip, bitrate hot-adjust, per-link ingest stats, and Stop. A persistent HUD bar shows four at-a-glance facts (live/idle/offline state, health verdict, bitrate, SoC temperature) across all destinations, with per-link signal detail and full telemetry available in an expanded sheet.
  • Network — connectivity overview. Bonded link status, WiFi networks (connect/disconnect/forget), cellular modems (APN, roaming, network type), Ethernet interfaces, hotspot configuration, and provider-aware Bluetooth controls. PipeWire images never try to start the retired BlueALSA unit, and a connected Bluetooth microphone is offered only when the installed provider agrees with the selected audio backend. Newly attached cellular hardware remains visible while modem services probe it. After two authoritative misses, only strong cellular evidence retains a non-actionable “Not controllable” row; descriptor-only guesses disappear and stay retired while attached, including across monitor restarts. Bluetooth descriptors never qualify by shape alone: wireless admission requires the full RNDIS triplet e00103, and ID_MM_DEVICE_IGNORE=1 always excludes the device. Successful SIM PIN, PUK, and PIN2 unlocks update the affected modem row immediately over the existing push channel, without a page reload. Calm info/warning bands surface interface-topology issues without ever blocking a connection: a same-subnet notice when two bonded links deliberately share a subnet (normal for policy-routed bonding), and a policy-route warning if a bonded WiFi/modem link is missing its expected routing table.
  • Settings — system and device configuration. All actions open focused dialogs: cloud remote, LAN password, SSH, logs, software updates, power, version info, and per-protocol network-ingest (RTMP/SRT) enable/disable. The software-update dialog now answers rather than going quiet: a check that could not reach the repositories, or that landed on a captive portal, says so instead of reporting "up to date"; the result names which address family worked when only one did; a package that ships with the next OS image or that apt kept back is listed as such rather than offered for install; and an update refused before it starts — most often for insufficient free space — reports its own reason and clears the progress overlay instead of leaving "Applying…" on screen.

A dev-only DevTools destination is available in development builds.

Other Highlights

  • Live session source switching: the cockpit can offer engine-reported synthetic fallback legs alongside capture legs without adding devices to discovery. It retains the legacy two-capture gate when the engine lacks the contract. Explicit empty rosters offer no switches; legacy unknown rosters have a distinct notice. The U6 hardware drill remains 0/10.

  • Verified dongle credentials: login submission verifies before saving to the existing permission-protected store. Failed attempts preserve a previous saved login; failed drafts remain only until the dialog closes. Portal reachability and rejected authentication have distinct messages in all ten locales. Router diagnostics remain reachable even without writable settings. See CONFIG_PERSISTENCE.md for protocol limitations.

  • Progressive Web App: offline capabilities and native app-like performance

  • Internationalization: 10 languages with full RTL support

  • Touch/kiosk mode: ?mode=touch URL flag scales touch targets to 44px minimum

  • Responsive: desktop and mobile layouts with a persistent bottom HUD dock on mobile

  • Acknowledged stream stops: the backend reports a stop only after cerastream confirms it is idle; an unrelated in-flight engine request cannot swallow Stop while the media session continues in the background. The request and cleanup budgets leave an explicit margin inside the existing 12-second bound, and a late, undispatched connection is closed without sending a stale stop. Failed stop IPC also completes local cleanup without claiming engine idle; restoration reconciles that state before admitting its one restart. Live composition disable now uses staged transactional reconfiguration rather than a save-only acknowledgement. The companion engine correction is released in cerastream 2026.9.5; CeraUI 2026.9.3 prepares the consumer release. Released-build product-path qualification remains separate from the candidate evidence; see composition lifecycle evidence.

  • Capability-gated AP+STA WiFi: proven radios can keep their station link while hosting a hotspot; unsupported or unreadable drivers retain the honest exclusive switch. The deterministic virtual interface is type-checked before reuse, and adapters with temporarily unresolved hardware identity remain visible as degraded rows rather than disappearing. Physical-radio validation is still pending; see docs/AP-STA-CONCURRENT-MODE.md.

  • Device Health telemetry: memory, per-cluster CPU frequency, DDR bus load, GPU load, and (on the vendor kernel) per-core decoder load, alongside the existing SoC temperature and load-average traces. Every signal is omitted rather than fabricated when its kernel interface is absent. A newly signed-in browser receives the latest completed device-stats reading immediately rather than waiting for the next periodic sample.

  • Media-load detail: the compact per-core hint uses the driver's reported encode/decode/JPEG/RGA inventory. MPP load and utilization remain separate, including values above 100%; Media details opens the full bound-session and provenance view. Older devices keep their percentage/busy/idle/unavailable display. Both-board visual validation remains outstanding; see docs/ENCODER-LOAD.md.

  • Hardware preview encoding: on capable boards (RK3588), an operator can toggle the local preview between software and hardware encode, with an honest fallback message when the board's encoder plugin is missing or rejects a setting. See docs/DEVICE-STATS-VALIDATION.md for the outstanding board-validation checklist (open, unrun as of writing).

  • Engine-owned codec offering: the Encoder dialog reads the live per-codec encoder ladder published by cerastream, including explicit 4K60 qualification state. Older snapshots retain a golden-pinned board fallback rather than losing their encode ceiling.

  • Per-uplink health: bounded device-specific checks feed default-route election. Host election prefers device-bound repository HTTPS, retaining an ordinary-connectivity fallback when no NIC passes. Apt waits for route repair and a fresh unbound family check before dispatch; see host uplink policy and limitations. Generic gateway checks race the first IPv4 and IPv6 targets with a 250 ms stagger instead of walking every DNS answer serially, while active SRTLA links use passive RTT/NAK telemetry instead of competing probes. Captive portals remain visible as degraded links.

  • Flow-sticky client sharing: the backend assigns new hotspot/shared-LAN flows across healthy uplinks while preserving established-flow affinity and keeping locally-originated SRTLA traffic outside its NAT path. The image carrier is the remaining deployment dependency; see docs/UPLINK_STEERING.md.

  • Streaming-first client shaping: while live, locally-originated SRT/SRTLA traffic occupies an uncapped priority band and only steering-marked client flows receive an adaptive CAKE/HTB ceiling. See docs/UPLINK_SHAPING.md.

Development

Commands

Command Description
bun run dev Start frontend + backend with mprocs TUI
bun run build Build for production
bun run --filter frontend dev Frontend only
bun run build:frontend Build frontend only

Mock Scenarios

Development mode includes hardware mocking. All mock state is Zod-validated at startup and can be reset between tests via resetMockState().

Mock modems identify their own USB-network interfaces with usb_modem_net, so Network lists each under Cellular once its roster row claims the interface. The interface name and address remain in that row's Details disclosure; ordinary Ethernet ports and isolated-dongle controls remain separate.

bun run dev                        # Default: 3 modems + WiFi (multi-modem-wifi)
bun run dev:single-modem           # 1 modem, no WiFi
bun run dev:streaming              # Active streaming simulation
bun run dev:modem-pin-locked       # 2 modems, modem 0 SIM PIN-locked (PIN 0000)
bun run dev:bt-mic-paired          # Bluetooth on, HFP mic already paired
MOCK_SCENARIO=streaming-active bun run dev  # Override inline
Scenario Modems WiFi Streaming
multi-modem-wifi 3 (5G/4G/3G) Yes Idle — Bluetooth on with nothing paired yet
single-modem 1 No Idle
streaming-active 3 Yes Active (with live telemetry)
modem-pin-locked 2 No Idle — modem 0 SIM PIN-locked (fixture PIN 0000); exercises the SIM unlock/PUK flow
bt-mic-paired 1 Yes Idle — Bluetooth on with an HFP mic already paired, trusted and connected (battery 80%)
caps-full 2 Yes Idle — full engine caps: H265 + hw accel, audio-capable source, live audio switch, SRT transport
engine-starting 1 No Idle — engine still booting, minimal safe floor + engineStarting flag
engine-unavailable 1 No Idle — engine unreachable, cached/minimal snapshot + engineUnavailable flag

The mock subsystem also simulates add-on state, kiosk state, SIM PIN/PUK lock states, Bluetooth (adapter, discoverable roster, pair/trust, a timed scan window), cerastream engine errors, and device-detection overrides. See apps/backend/src/mocks/ for the fixture factory and schema definitions.

Environment Variables

In production the WebSocket RPC URL is derived purely from the page origin (window.location): the single backend binary serves the static frontend and the WebSocket on the same host and port, so no socket host/port/protocol is configured and the VITE_SOCKET_* variables are ignored. The variables below apply to development only, where Vite (:6173) and the backend (:3002) are separate origins; all are optional.

These dev-only values live in the tracked .env.development file, which Vite loads only in mode === "development" and the bun run dev scripts load via dotenv -e .env.development. A production build never reads .env.development, so none of these can reach the shipped bundle. The gitignored root .env is loaded in every Vite mode, so it must stay absent — a CI guard fails the build if a stray .env bakes a ws://localhost literal into dist/public. NODE_ENV is not read from any env file (Vite sets the frontend mode; the backend dev scripts export it inline). See .env.example for the full layout.

Variable Scope Home Dev Default Description
VITE_SOCKET_ENDPOINT dev only .env.development ws://<page hostname> WebSocket endpoint (scheme + host, no port)
VITE_SOCKET_PORT dev only .env.development 3002 Backend dev WebSocket port
MOCK_SCENARIO dev only .env.development multi-modem-wifi Hardware mock scenario
LOG_LEVEL dev + prod shell / systemd env (per-transport default) Override Winston log level for all transports. Dev console default: info; prod console: warn; file: debug. Set to debug to enable per-RPC call trace lines.

Build & Deploy

Debian Package

bun run test:release-package-contracts
BUILD_ARCH=arm64 ./scripts/build/build-debian-package.sh
BUILD_ARCH=amd64 ./scripts/build/build-debian-package.sh

Release assets are published through publish-release.yml; see docs/BUILD_PIPELINE.md for the stable APT handoff.

CeraUI pins the published @ceralive/cerastream@2026.9.11 in both the backend and shared RPC package, carrying schema 0.21.0's capture status, failover-rate policy, raw-input classifications, and source-mode session-switch fields. The producer's exported ChangeConfigParams and installed schema are regression-tested directly. The bindings-skew gate checks the schema version and preservation of the three new HDMI capture causes. The released 2026.9.1 build retains the early-import SIGUSR1 window. The current source closes the ordered add-on poke's startup race with a systemd readiness barrier; see Boot readiness. This is not a claim of full board qualification.

Supported Hardware

ARM64: Orange Pi 5/5+, Radxa Rock 5B AMD64: Intel N100/N200 Mini PCs, standard x86 computers

See BUILD_PIPELINE.md for full build documentation.

Documentation

Document Description
LIVE_DEVELOPMENT.md Local dev, deploy to device, image build, debugging
ARCHITECTURE.md System overview and data flow diagrams
BUILD_PIPELINE.md Build system and CI/CD
APT_VERSION_CONTROL.md Debian package versioning
BRANDING.md Branding guidelines
TOUCHSCREEN.md Touch/kiosk layout mode
UPLINK_STEERING.md Shared-client flow steering, route/NAT ownership, and validation status
UPLINK_SHAPING.md SRT-priority qdisc hierarchy, adaptive client caps, ownership, and validation status
TECHNICAL_DEBT.md Machine-checkable tech-debt register (source-experience overhaul)
CONVENTIONS.md CeraUI-local conventions including tech-debt register contract
apps/frontend/docs/DEVTOOLS.md Development tools

Workspace Commands

bun add [package] --cwd apps/frontend  # Add dependency to frontend
bun add [package]                      # Add shared dependency (root)
bun run clean                          # Clean all build artifacts

Tech Stack

  • Frontend: Svelte 5, TailwindCSS v4, shadcn-svelte (bits-ui v2), Vite
  • Backend: Bun, TypeScript, WebSocket RPC (oRPC)
  • Build: Bun workspaces, mprocs

Support the Project

If you find CeraUI useful, consider supporting CeraLive development:

UVC package integration

Software updates classify gstreamer1.0-libuvcsrc as an application package. The portable libuvc source supports H.264/H.265; it is separate from RK3588's MPP/RGA acceleration. Existing engine source IDs and factory aliases stay intact.

About

On-device control plane for CeraLive — Svelte 5 PWA + Bun/oRPC backend driving ceracoder and srtla via native TypeScript bindings

Resources

Stars

13 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages