Skip to content

Configuration

Rust-Srec uses a 4-layer configuration hierarchy for flexible control. See Configuration Layers for detailed architecture.

Basic Configuration

Adding Your First Streamer

  1. Open the frontend at http://localhost:15275
  2. Log in with default credentials:
    • Username: admin
    • Password: admin123!
  3. Navigate to StreamersAdd Streamer
  4. Enter:
    • Name: Display name
    • URL: Direct channel URL (e.g., https://live.bilibili.com/<room-id>)
    • Platform: Auto-detected from URL
  5. Keep Enable monitoring on and click Create streamer

For a complete success check, follow Make Your First Recording.

Global Settings

Access via SettingsGlobal Config. The settings are organized into several categories:

File Configuration

SettingDescriptionDefault
record_danmuEnable danmaku (live chat) recordingfalse
danmu_statisticsHow chat activity is summarised per session (see below)defaults
auto_thumbnailAutomatically generate video thumbnailstrue
output_folderBase directory for recordings (supports templates)/app/output
output_filename_templateFilename pattern for recorded files(see below)
output_file_formatDefault container format (mp4, flv, etc.)flv

Danmu Statistics

Every recording with record_danmu on gets a per-session chat summary: totals, an activity timeline, the most active chatters, the most frequent words and — where the platform reports them — gift rankings. danmu_statistics tunes that summary, and can be set globally or overridden per platform, per template and per streamer. Any field you leave out keeps its default, so {"top_talkers": 200} is a complete override.

FieldDescriptionDefault
enabledCompute the summary at all. Turning it off still records the chat files; it only stops the summary, which stores viewer names, from being computed and saved.true
top_talkersChatters and gift senders listed per session (1–500)100
top_wordsFrequent words listed per session (1–500)50
top_giftsGift names listed per session (1–500)20
rate_bucket_secsActivity-timeline granularity in seconds. Very long streams are automatically coarsened, so the session page reads the width back rather than assuming it.10
talker_capacityDistinct chatters tracked (64–8192). While a stream has fewer than this, counts are exact; above it they become close estimates and the session page marks them with .2048
word_capacityDistinct words tracked (64–8192), same trade-off2048
gift_capacityDistinct gift names tracked256
extra_stop_wordsWords to exclude from the frequent-words chart, on top of the built-in listnone

Out-of-range values are clamped rather than rejected, and a reported list is never longer than what is tracked.

Resource Limits

SettingDescriptionDefault
min_segment_sizeMinimum size before a segment is kept1MB
max_download_duration_secsMax duration before splitting the recording0 (disabled)
max_part_sizeMax size before splitting the recording8GB

Concurrency & Performance

SettingDescriptionDefault
max_concurrent_downloadsMax simultaneous recording tasks6
max_concurrent_uploadsMax simultaneous upload tasks3
max_cpu_jobsMax concurrent CPU-intensive tasks0 (Auto)
max_io_jobsMax concurrent I/O-intensive tasks8 (0 = Auto)
download_engineEngine used for recording (ffmpeg, mesio, etc.)mesio
queue_freshness_thresholdWhen a recording has been waiting for a free slot longer than this, rust-srec re-checks the streamer to refresh stream URLs and headers before starting. Useful on platforms whose signed URLs expire within minutes. Set to 0 to refresh on every queue wait.60 Secs

Network & System

SettingDescriptionDefault
streamer_check_intervalInterval between checking streamer status60 Secs
offline_check_intervalInterval between checking offline status20 Secs
offline_detection_countConsecutive offline checks before confirming the streamer is offline. The same resolved count controls when consecutive download failures enter temporary cooldown. Download failures use a minimum threshold of 2.3
retention_periodNumber of days to keep recordings in history30 Days
enable_proxyRoute traffic through an intermediate serverfalse

Pipeline Configuration

Rust-Srec features a powerful modular pipeline system where you can add custom steps (e.g., transcripts, notifications, custom scripts) at different stages:

  • Per-segment: Runs for each recorded segment.
  • Paired Segment: Runs for video/danmaku pairs.
  • Session Complete: Runs when the entire recording session ends.

Folder Organization

Set output_folder to {streamer}/%Y-%m-%d to organize recordings by streamer with date-based subfolders. The output_filename_template can then use %H-%M-%S_{title} for the filename itself.

Environment Variables

The following environment variables can be configured in your .env file.

General

VariableDescriptionDefault
TZContainer timezoneUTC
VERSIONDocker image version taglatest

Paths

VariableDescriptionDefault
DATA_DIRDirectory for application data./data
CONFIG_DIRDirectory for platform configuration files./config
OUTPUT_DIRDirectory where recordings are stored/app/output
LOG_DIRDirectory for log files./logs

Shutdown

VariableDescriptionDefault
RUST_SREC_SHUTDOWN_TIMEOUT_SECSStrict standalone-server shutdown deadline30
RUST_SREC_SHUTDOWN_FORCE_RESERVE_SECSTime reserved inside the deadline for forced process-tree containment; must be greater than zero and less than the total timeout2
RUST_SREC_CONTAINER_STOP_GRACE_PERIODDocker Compose wait before external SIGKILL; keep longer than the backend deadline35s
RUST_SREC_RUNTIME_MARKER_PATHDirty-generation marker retained after a forced or crashed runtimeBeside the SQLite database

The deadline starts when the parent observes SIGINT or SIGTERM, including while startup admission or marker I/O is in progress, and covers the parent process exit as well as worker cleanup. The server first asks its isolated runtime to shut down gracefully; the runtime's own drain budget is derived from these same two values (the timeout minus the force reserve, less a small scheduling margin), so raising the timeout lengthens the phase that actually finalizes recordings. At the start of the force reserve, it terminates the contained process tree if the runtime is still active and exits unsuccessfully. Exit status 124 identifies hard-deadline expiry; 125 means the terminal process-tree termination request itself failed. A worker-local fatal failure fails closed instead of starting an unbounded graceful drain. A retained marker means startup recovery may be required; later clean runs do not erase that earlier recovery debt. The marker does not by itself reconstruct an artifact that was interrupted before it reached SQLite. Remove it only while the backend is stopped and after the interrupted artifacts have been reconciled. Recording engines clamp their own graceful-stop wait to whatever remains of this budget, so a per-engine stop timeout that is longer than the shutdown timeout no longer causes the engine child to be killed mid-finalization. A shutdown that overruns its grace period but still finalizes everything exits cleanly; only work that could not be contained is reported as a crash.

Network

VariableDescriptionDefault
API_BIND_ADDRESSIP address the backend API binds to0.0.0.0
API_PORTExternal port for the backend API12555
FRONTEND_PORTExternal port for the web interface15275
BACKEND_URLInternal URL for the frontend to reach the backendhttp://rust-srec:8080
HTTP_PROXYHTTP proxy server URL-
HTTPS_PROXYHTTPS proxy server URL-
NO_PROXYComma-separated list of hosts to bypass proxy-

Security & Auth

VariableDescriptionDefault
JWT_SECRETSecret key for JWT signing (Required unless using the local-only opt-out below)-
AUTH_DISABLEDDisable backend authentication for loopback-only local developmentfalse
API_CORS_ORIGINSComma-separated exact browser origins (scheme://host[:port]) allowed to call the API cross-origin while authentication is disabledLocal dev server and desktop webview origins
API_LOGIN_MAX_FAILURESFailed logins tolerated per account inside the window5
API_LOGIN_IP_MAX_FAILURESFailed logins tolerated per source address inside the window100
API_LOGIN_WINDOW_SECSLength of the failed-login window, in seconds900 (15m)
JWT_ISSUERJWT issuer identifierrust-srec
JWT_AUDIENCEJWT audience identifierrust-srec-api
SESSION_SECRETFrontend session encryption secret (Required, min 32 chars)-
COOKIE_SECURESet to true to force HTTPS-only cookies(auto)
MIN_PASSWORD_LENGTHMinimum length for user passwords8

The backend refuses to start without a non-empty JWT_SECRET. For local development only, authentication can be disabled by setting both AUTH_DISABLED=true and API_BIND_ADDRESS=127.0.0.1 (or ::1). The backend rejects this opt-out for wildcard, hostname, and non-loopback bind addresses.

While authentication is disabled, only the origins in API_CORS_ORIGINS may call the API from a browser; the default list covers http://localhost:15275, http://127.0.0.1:15275, http://[::1]:15275, tauri://localhost, and http://tauri.localhost. Set the variable to override it — entries must be exact origins with no trailing path, and malformed entries are skipped with a warning at startup. Requests from any other origin are refused with 403, as are requests whose Host header is neither a loopback name nor the configured bind address. With authentication enabled the variable is ignored and any origin may send requests, because every protected route still requires a bearer token.

Login throttling

POST /api/auth/login counts failed attempts in a sliding window and answers 429 with a Retry-After delay once a budget is spent. Two budgets apply to every attempt:

  • Per account (API_LOGIN_MAX_FAILURES, default 5). A successful login clears it immediately.
  • Per source address (API_LOGIN_IP_MAX_FAILURES, default 100). This one is deliberately loose. The source address is the peer of the TCP connection, and X-Forwarded-For is not trusted, so behind the bundled frontend container, nginx, or any other reverse proxy every login arrives from the proxy's address. Treat this budget as a cap on password-hashing work, not as a per-user lockout — while it is exhausted, everyone behind that proxy is throttled. Raise it if that matters more to you than the hashing cap; lower it only if browsers reach the backend directly.

Both share the window length set by API_LOGIN_WINDOW_SECS.

Token Expiration

VariableDescriptionDefault
ACCESS_TOKEN_EXPIRATION_SECSJWT access token lifetime3600 (1h)
REFRESH_TOKEN_EXPIRATION_SECSJWT refresh token lifetime604800 (7d)

Browser Notifications (Web Push / VAPID)

VariableDescriptionDefault
WEB_PUSH_VAPID_PUBLIC_KEYVAPID public key (base64url, unpadded). Leave empty/unset to disable.-
WEB_PUSH_VAPID_PRIVATE_KEYVAPID private key (base64url, unpadded). Leave empty/unset to disable.-
WEB_PUSH_VAPID_SUBJECTVAPID subject (e.g. mailto:admin@localhost)mailto:admin@localhost

Backend Service

VariableDescriptionDefault
RUST_LOGLogging level (trace, debug, info, warn, error)info
DATABASE_URLSQL database connection stringsqlite:///app/data/rust-srec.db
RUST_SREC_LOCALELocale for backend-emitted notification strings. Affects every notification event — stream online/offline, download lifecycle, segments, pipeline jobs, system alerts, credential events. Supported: en, zh-CN.en
RUST_SREC_OUTPUT_ROOTSComma-separated list of absolute paths to treat as output-root boundaries for the write gate. If unset, the gate uses a heuristic that takes the first two named components of each resolved output path (e.g. /rec/huya for /rec/huya/X/20260415, /home/user for /home/user/recordings/X/20260415). Two named components is the smallest safe default — it avoids accidentally sharing a gate key across unrelated users in /home/... layouts. For a single-mount /rec-style layout where you want one gate key per mount (and therefore one aggregated notification on failure instead of one per platform), set this explicitly: RUST_SREC_OUTPUT_ROOTS=/rec.-

Resource Limits (Docker)

VariableDescriptionDefault
CPU_LIMITMaximum CPUs the container can use4
MEMORY_LIMITMaximum memory the container can use4G
CPU_RESERVATIONReserved CPUs for the container1
MEMORY_RESERVATIONReserved memory for the container512M

Filename Template Variables

Rust-Srec supports two types of placeholders in output_folder and output_filename_template.

Curly Brace Variables

These are replaced with streamer or session specific metadata.

VariableDescription
{streamer}Streamer display name
{title}Current stream title
{platform}Platform name (e.g., bilibili)
{session_id}Unique ID for the recording session (only in output_folder)

Percent Placeholders (FFmpeg Style)

These are replaced with date, time, or sequence information.

VariableDescription
%YYear (YYYY)
%mMonth (01-12)
%dDay (01-31)
%HHour (00-23)
%MMinute (00-59)
%SSecond (00-59)
%iSequence number for split parts
%tUnix timestamp
%%Literal percent sign

Example: {streamer}/%Y-%m-%d/%H-%M-%S_{title}

Pipeline Destination Placeholders

Pipeline destination fields such as rclone destination_root and copy/move destination support {platform}, {streamer}, {title}, {streamer_id}, {session_id}, and the same %Y, %m, %d, %H, %M, %S, %t, and %% time tokens. Time tokens render in the server's local time zone.

Rclone expands time tokens with the job creation time by default. Set time_anchor to session_start to keep every segment from one live session in the folder for the session's start date, even when the stream crosses midnight. Copy/move preserves its historical execution-time expansion when time_anchor is omitted; set it to job_created or session_start when deterministic anchoring is needed.

When anchoring by session start, keep %Y%m%d-%H%M%S or %t in the filename template. If multiple sessions send the same basename into one destination folder, rclone and filesystem copy/move operations can overwrite or skip files depending on the operation and arguments.

Released under the MIT License.