@kumix/worker
v0.4.7
Published
Kumix Worker is a self-hosted live-stream runner that schedules and broadcasts video sources to RTMP platforms with always-on source looping, monitoring, crash recovery, and a local dashboard.
Downloads
335
Maintainers
Readme
Kumix Worker
Self-hosted live streaming on autopilot.
Kumix Worker is a self-hosted live-stream runner that supports scheduling, always-on source looping, and broadcasting video sources to RTMP/RTMPS platforms with monitoring, crash recovery, and a local dashboard.
Package: @kumix/worker
CLI: kumix-worker
Quick Start
One-line install (Ubuntu / Debian VPS)
curl -fsSL https://raw.githubusercontent.com/kumixlabs/worker/main/install.sh | sudo bashThis runs apt update && upgrade, installs Node.js 24 + FFmpeg, installs the worker, configures systemd with auto-start, and prints the dashboard URL. Everything in one command — just open http://<server-ip>:8080 when it's done.
Must run as root (sudo). Without sudo it will refuse and tell you to re-run with sudo.
Options:
# Custom port + timezone
curl -fsSL https://raw.githubusercontent.com/kumixlabs/worker/main/install.sh | sudo bash -s -- --port 9090 --timezone UTC
# Uninstall everything (service + package + data)
curl -fsSL https://raw.githubusercontent.com/kumixlabs/worker/main/install.sh | sudo bash -s -- --uninstallNPM (global install)
npm install -g @kumix/worker
kumix-worker serveDocker
docker run -d \
-p 8080:8080 \
-v "$HOME/.kumix-worker:/app/data" \
-e KUMIX_WORKER_DATA_DIR=/app/data \
--name kumix-worker \
kumix/worker:latestSee DOCKER.md for full Docker usage, compose, and configuration.
What It Does
- Runs a local web dashboard for managing live stream jobs.
- Stores data locally in SQLite under the worker data directory.
- Downloads and validates video sources from direct URLs and Google Drive shared links.
- Probes media with FFprobe and validates H.264/AAC with max video bitrate 35 Mbps.
- Manages RTMP targets with encrypted stream keys.
- Creates manual, scheduled, and recurring stream jobs.
- Runs FFmpeg jobs and tracks runtime metrics, status, logs, and tombstones.
- Recovers interrupted streams safely after restart.
- Exposes health, stats, capabilities, link metadata, and token rotation endpoints for external integrations.
- Serves static dashboard assets from the package build.
Main Features
Dashboard
The dashboard includes:
- Overview dashboard with live streams, scheduled streams, attention items, and recent activity.
- Monitoring page for CPU, memory, bandwidth, disk, FFmpeg, FFprobe, scheduler, and process status.
- Log page with live SSE updates, stream/kind/search filters, pause/resume, export, and clear actions.
- Sources page for adding direct URL or Google Drive sources, viewing media details, previewing, renaming, cancelling, retrying, and deleting sources, including bulk delete.
- Targets page for creating/editing RTMP targets and enabling/disabling destinations.
- Streams page for stream lifecycle actions, logs, exports, stopped time edits, and safe deletion.
- Settings page for timezone, disk usage limit, optional YouTube Data API key (write-only), dashboard password change, and API token regenerate.
- EN/ID i18n with parity and orphan-key tests.
Sources
Sources support:
- Direct URL video sources.
- Google Drive shared links.
- Safe Google Drive file ID parsing.
- SSRF protection with DNS resolution checks, per-redirect-hop validation, and connection-time DNS pinning so the request only connects to the vetted public address.
- Download size limits, configured disk-usage-limit enforcement, and cleanup on failure.
- FFprobe metadata extraction with a probe timeout.
- Codec and bitrate validation with
format.bit_ratefallback. - Local cache storage, removed from disk when a source is deleted.
- Concurrent download+probe limited to protect disk/CPU.
- Details dialog with duration, resolution, FPS, codecs, bitrate, keyframe interval, and invalid reason.
Validation rules:
- Video codec: H.264 / AVC1.
- Audio codec: AAC / MP4A.
- Max video bitrate:
35000 kbps/35 Mbps.
Targets
Targets support:
- Label, RTMP/RTMPS ingest URL, encrypted stream key.
- Edit label/ingest URL and optionally replace stream key.
- Active/disabled state.
- Created date column and newest-first sorting.
- Bulk delete for selected rows.
- Token rotation re-encrypts stored target secrets.
Streams
Streams support:
- Manual start jobs.
- Scheduled start time.
- Optional auto-stop time.
- Recurrence: none, daily, weekly, monthly.
- Persistent status tracking:
pending,running,stopping,stopped,failed. - Runtime metrics from FFmpeg stderr.
- Live stop action and crash-safe tombstones.
- Safe delete blocking for running/stopping streams.
- Stream-specific log export as a text file.
Stream action matrix:
| Status | Actions |
| ---------- | -------------------------------------------------------- |
| pending | View Log, Export Log, Edit, Delete |
| running | View Log, Export Log, Stop, Edit (YouTube Live URL only) |
| stopping | View Log, Export Log, Edit (YouTube Live URL only) |
| stopped | View Log, Export Log, Edit, Delete |
| failed | View Log, Export Log, Start, Edit, Delete |
Edit is available on every status so you can attach or update a YouTube Live URL for analytics without recreating the stream. While running or stopping, only the YouTube Live URL field is editable; title, source, target, and schedule stay locked. Source video always loops until stop or auto-stop.
Logs And Events
Events support:
- Recent event listing, capped at the most recent 200 events.
- Global event SSE with a 15-second keepalive heartbeat.
- Stream-specific event SSE.
- Global event export as text.
- Stream event export as text.
- Clear all logs confirmation.
- Short-lived signed URLs for browser-only SSE/export flows.
- Event storage capped at 5000 rows with automatic pruning.
Core-Facing API
External integrations should use /api/v1/* endpoints with Bearer token auth.
Available core-facing endpoints:
GET /api/v1/health- lightweight worker health.GET /api/v1/stats- monitoring stats and recent stream summaries.GET /api/v1/capabilities- worker API version, feature flags, limits, and safe settings.GET /api/v1/link- link/install metadata without leaking the raw token.POST /api/v1/settings/token- rotate worker token and re-encrypt target secrets.
CORS origins are not allowed by default. Configure allowed origins with KUMIX_WORKER_CORS_ORIGINS.
CLI
Common commands:
kumix-worker init
kumix-worker serve
kumix-worker status
kumix-worker doctor
kumix-worker token
kumix-worker token --show
kumix-worker token --regenerate
kumix-worker password --password <new-password>
kumix-worker reset --yes
kumix-worker reset --all --yes
kumix-worker reset --force --yes
kumix-worker updatekumix-worker update supports --check, --restart, --force, and --auto-start.
Note:
kumix-worker updateonly works for NPM installs. To update a Docker deployment, pull the latest image and recreate the container — see DOCKER.md.
Development commands:
bun install
bun install --cwd frontend
bun run dev
bun run build
bun run start
bun run lint
bun run lint:fix
bun run format
bun run format:check
bun run types:check
bun run test
bun run test:watch
bun run test:coverage
bun run bumpRun commands from the repository root. Root and frontend dependencies require separate installs.
bun run dev starts:
- API on
http://localhost:8080 - Vite dashboard on
http://localhost:8000(proxies/apito the worker)
Runtime Data
Default data directory:
~/.kumix-workerData layout:
~/.kumix-worker/
config.json
db/
db.sqlite
cache/
tombstones/Config contains:
token— API Bearer key, stream-key encryption root, signed-URL HMACpasswordHash— scrypt hash of the dashboard login passwordporttimezonediskUsageLimitPercentyoutubeApiKey— optional; never returned raw from the APIdataDir
The config file is written with restrictive permissions where supported.
Environment Variables
KUMIX_WORKER_DATA_DIR
KUMIX_WORKER_PORT
KUMIX_WORKER_TIMEZONE
KUMIX_WORKER_IPV4_FIRST
KUMIX_WORKER_TRUST_PROXY
KUMIX_WORKER_DISK_LIMIT_PERCENT
KUMIX_WORKER_MAX_DOWNLOAD_BYTES
KUMIX_WORKER_DOWNLOAD_TIMEOUT_MS
KUMIX_WORKER_CORS_ORIGINS
KUMIX_WORKER_FFMPEG_PATH
KUMIX_WORKER_FFPROBE_PATH
KUMIX_WORKER_AUTO_RESUMEKUMIX_WORKER_FFMPEG_PATH and KUMIX_WORKER_FFPROBE_PATH override the bundled static binaries with a system FFmpeg/FFprobe. Set these when the static build cannot resolve DNS for RTMP output (it can segfault on some hosts because statically linked glibc cannot load NSS modules). When unset, the bundled ffmpeg-static/ffprobe-static binaries are used.
KUMIX_WORKER_AUTO_RESUME defaults to on. On graceful stop (SIGTERM/SIGINT, e.g. Docker stop or compose recreate), active streams are marked and started again after boot. Set to 0 to disable.
HTTP API Overview
Dashboard/private API routes use Bearer token auth:
/api/settings,PATCH /api/settings,POST /api/settings/password/api/stats/api/metrics/api/health/details/api/bandwidth/api/sources/api/targets/api/streams/api/events/api/events/signed-url/api/sources/:id/preview-url
The cached source preview (GET /api/sources/:id/preview, with HTTP range support) is authorized through a short-lived signed URL rather than a Bearer header, so the dashboard <video> element can stream it directly.
Public unauthenticated routes:
GET /healthGET /api/bootstrapGET /openapiGET /docsGET /auth?token=...(CLI/core handoff only)POST /api/auth/login(dashboard password)POST /api/auth/exchange(handoff code → session token)
Signed URL routes are generated by POST /api/events/signed-url and POST /api/sources/:id/preview-url, and are short-lived.
Security Notes
- Dashboard login uses a password (factory default
123456, scrypt-hashed). First login with the default forces a password change in the SPA. Change anytime under Settings or withkumix-worker password --password <pw>. Password change does not rotate the API token, re-encrypt stream keys, or invalidate other Bearer sessions. - Password hashing is async scrypt with allowlisted cost parameters (rejects corrupt/malicious
passwordHashvalues that could DoS the process). InvalidpasswordHashfails closed (no silent reset to default). - API routes require Bearer token auth unless explicitly public. After password login (or handoff), the SPA stores the worker token in localStorage and sends it as Bearer. On 401 the SPA clears the session.
- Core handoff:
/auth?token=validates the token, then redirects with a single-use, short-lived#code=that the dashboard exchanges viaPOST /api/auth/exchange. CLI printed dashboard URLs never embed the token (password login). - Token rotation (
POST /api/v1/settings/tokenorkumix-worker token --regenerate) returns{ rotatedAt, tokenLength }only — never echoes the new token. Concurrent rotations are serialized; stream keys are re-encrypted with rollback on config write failure. - Invalid auth attempts are rate-limited (10 / 60s / IP), with lazy expiry and prune. Buckets key on the socket address by default; forwarded headers are only trusted when
KUMIX_WORKER_TRUST_PROXY=1(enable only behind a proxy that strips client-supplied XFF). - Web/core API calls are rate-limited separately.
/api/*routes enforce a 1 MB request body limit; unknown/api/*paths return a 404 envelope.- Responses include security headers:
X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Referrer-Policy: no-referrer, and a basic Content-Security-Policy. - New dashboard passwords cannot be the factory default (
123456); rejected by API and CLI. - Stream keys are encrypted using the worker token (AES-256-GCM). FFmpeg is spawned with the RTMP URL (including the stream key) as argv — deploy single-tenant or in a container so process listings are not shared.
- Raw worker token and password hash are never returned from settings or core-facing endpoints. Settings expose
passwordIsDefault/hasPassword(booleans) andtokenLengthonly. - Raw and encrypted target stream keys are never returned from API responses; only a masked preview is exposed.
- Source downloads are protected against SSRF via DNS checks, per-redirect-hop validation, and connection-time DNS pinning that blocks private, loopback, link-local, and embedded-IPv4 (6to4/NAT64) addresses. Google Drive confirmation failures do not fall back to HTML quarantine pages. Concurrent downloads are capped.
- Static file serving guards against path traversal and streams assets.
- Source cache filenames and event export filenames are sanitized.
- Config writes are atomic. Crash recovery avoids terminating reused PIDs after a reboot. Graceful shutdown stops streams and closes the HTTP server with a timeout so SSE clients cannot hang exit.
- Destructive data reset refuses unsafe directories.
CI And Release
GitHub Actions:
ci.ymlruns install, typecheck, lint, tests, and build on pull requests and main pushes.release.ymlpublishes to NPM onv*tags, and builds/pushes multi-platform Docker images to GHCR and Docker Hub.
NPM publishing requires NPM_TOKEN repository secret.
Release tags use:
vX.Y.ZVerification
Before finishing meaningful changes, run:
bun run types:check
bun run lint
bun run test
bun run buildThe test suite covers:
- Config validation, including weak token rejection and password hash seeding.
- Password helpers (async scrypt hash/verify, corrupt-param rejection).
- DB integration and stats aggregation.
- HTTP API CRUD, auth login/password change, stream-key non-exposure, running-stream delete protection, and SSE signed URL flows.
- Auth rate limits and security response headers.
- Core-facing API contract.
- Static serving security.
- FFmpeg/FFprobe helpers.
- Source downloading and SSRF validation.
- Scheduler, recurrence, and tick lifecycle.
- Recovery/tombstones.
- Crypto/token re-encryption.
- Token verification (timing-safe comparison).
- Version comparison for self-update.
- Stream runner lifecycle.
- Frontend message parity and orphan keys.
- Frontend smoke checks.
Troubleshooting
Stream immediately fails or segfaults
The bundled FFmpeg/FFprobe binaries are statically linked against glibc. On some hosts, statically linked glibc cannot load NSS modules, which causes the binary to segfault when resolving DNS for RTMP output. If a stream starts but immediately fails with a segfault or an unclear error, install a system FFmpeg and override the bundled binaries:
# Debian/Ubuntu
sudo apt install ffmpeg
# Tell Kumix Worker to use the system binaries
export KUMIX_WORKER_FFMPEG_PATH=/usr/bin/ffmpeg
export KUMIX_WORKER_FFPROBE_PATH=/usr/bin/ffprobe
kumix-worker serveThe official Docker image already installs system FFmpeg/FFprobe via apt and sets KUMIX_WORKER_FFMPEG_PATH / KUMIX_WORKER_FFPROBE_PATH to /usr/bin/*, so the container does not rely on the bundled static binaries for RTMP output.
Config is missing its token
If the worker refuses to start because the token is missing, it is refusing to generate a new one to avoid making existing encrypted stream keys undecryptable. Restore the original config.json or run a factory reset:
kumix-worker reset --all --yes
kumix-worker init