Risuko
Configuration

Configuration

Risuko configuration reference — all available options.

Risuko stores configuration in two JSON files in the config directory:

  • system.json — Engine options such as dir, split, doh-*, and rpc-secret
  • user.json — App preferences plus a fixed set of user-scope engine keys

The desktop app creates both files on first run. The CLI and Node.js API never create them; missing files or keys fall back to built-in defaults in memory.

The engine does not merge the two files wholesale. It starts from system.json, copies only the user-scope keys from user.json, then applies engine-overrides. Any other key in user.json is ignored by the engine, even if it has the same name as an engine option.

User Config

These user.json keys override system.json when the engine starts:

rpc-host, m3u8-output-format, keep-seeding, bt-create-subfolder, bt-enable-upnp, bt-upnp-lease, bt-encryption-policy, bt-listen-v6, bt-enable-lsd, purge-record-on-start, task-routing-rules, file-category-dirs, max-worker-retries, usenet-profiles, usenet-archive-limits, usenet-cleanup-mode, pbh-enable, pbh-listen-port, pbh-rpc-secret, and proxy (flattened into all-proxy / p2p-*).

KeyDefaultDescription
engine-overrides—Object of arbitrary engine keys applied on top of the global options at startup, so backend options work without a dedicated form field. Keys starting with rpc- or ending in -port or -secret are skipped; a non-object value is ignored with a warning

Config Directory Locations

EnvironmentPath
macOS~/Library/Application Support/<name>
Linux$XDG_CONFIG_HOME/<name> (~/.config/<name> when unset)
Windows%APPDATA%\<name>

<name> depends on the host:

HostDirectory name
Desktop appapp.risuko.Risuko
Headless mode of the app binary (risuko serve, risuko download and similar)app.risuko.Risuko, same as the desktop app; falls back to the legacy dev.risuko.app only when app.risuko.Risuko does not exist and dev.risuko.app does
Standalone risuko CLI and Node.js APIapp.risuko.Risuko on a fresh install; an existing legacy dev.risuko.app directory wins, so older installs keep their config and session. EngineConfig.configDir overrides it in Node.js

The standalone CLI and Node.js API therefore share the desktop app's directory on fresh installs, but keep using dev.risuko.app if it already exists.

Reading Configuration

CLI

# List all settings
risuko config list

# Get a specific value
risuko config get dir

# JSON output
risuko config list --json

Node.js

import risuko from "@risuko/risuko-js";

const options = await risuko.getGlobalOption();
console.log(options.dir);
console.log(options["max-concurrent-downloads"]);

Writing Configuration

CLI

# User-scope keys can be set directly
risuko config set rpc-host 0.0.0.0
risuko config set keep-seeding true

# Engine keys go through engine-overrides (replaces the whole object)
risuko config set engine-overrides '{"dir":"/home/user/downloads","max-concurrent-downloads":5}'

Values are parsed as JSON; anything that isn't valid JSON is stored as a plain string.

risuko config set always writes user.json. Setting an engine key such as dir or max-concurrent-downloads directly has no effect on the engine, although config get and config list (which layer user.json over system.json) will show the new value. Use engine-overrides, or edit system.json for the keys it skips (such as rpc-secret and rpc-listen-port).

Node.js

import risuko from "@risuko/risuko-js";

await risuko.changeGlobalOption({
  dir: "/home/user/downloads",
  "max-concurrent-downloads": "5",
});

changeGlobalOption changes the running engine only; nothing is written to the config files, so the values are lost at stopEngine.

Option Reference

General

KeyDefaultDescription
dirOS defaultDefault download directory
max-concurrent-downloads5Maximum active non-torrent download workers. The desktop app can raise this up to 10; the engine uses the value directly and preempts lower-priority non-torrent tasks back to waiting when the queue order requires it
continue—Removed. Still accepted but has no effect; interrupted downloads always resume
pausefalsePer-task: add a non-torrent task in the paused state
purge-record-on-startfalseWhen the engine starts, drop completed/stopped task records from the session and clear desktop download-history stats. On-disk files for stopped torrent tasks are preserved (the orphan-purge step is told to keep files for those info-hashes); HTTP/FTP/etc. payloads on disk are never touched

Desktop App

These keys are read by the Tauri desktop app. They are stored in user.json and are not part of the engine's JSON-RPC surface.

KeyDefaultDescription
auto-check-updatefalseAutomatically check the Tauri updater endpoint for signed release updates. Desktop-only and opt-in on every platform
last-check-update-time0Last update-check timestamp recorded by the app
legal-acceptedfalseFirst-launch Terms/Privacy acceptance flag; device-local and excluded from cloud sync
clipboard-watchtrue on desktopWatch the clipboard for download links and show a prompt. Not created on Android
clipboard-watch-extensionsarchive, installer, media, document, .torrent, meta4, metalink extensionsHTTP(S) file extensions that trigger the clipboard prompt. Protocol links such as magnet, ED2K, FTP/SFTP, M3U8, and thunder:// do not need an extension match
clipboard-watch-notice-seenfalse on desktopDevice-local flag for the one-time clipboard-watch notice

Connection

KeyDefaultDescription
split16Connections per download (HTTP/HTTPS); ignored when the file is smaller than min-split-size or the server doesn't support Range
max-connection-per-serversplitCap on connections to one server per download (aria2-compatible)
max-worker-retries5Retries per HTTP segment worker before the task fails (user.json)
min-split-size1MMinimum segment size before the file is split (bytes; accepts K/M/G suffixes)

Speed Limiting

KeyDefaultDescription
max-overall-download-limit0One overall download budget (bytes/s, 0 = unlimited) shared by HTTP/FTP/SFTP/HLS, BitTorrent, ED2K, and Usenet. Applied live to running downloads, no restart needed
max-overall-upload-limit0Overall upload cap (bytes/s, 0 = unlimited), enforced for BitTorrent uploads. Combined with bt-upload-rate-limit: the smaller non-zero value wins. Applied live
max-download-limit0Per-task download speed limit (bytes/s). Applies to HTTP, m3u8, FTP, torrent, ED2K, and Usenet downloads; retuned live by changeOption
max-upload-limit0Per-task upload speed limit (bytes/s) for torrents; retuned live by changeOption

HTTP

KeyDefaultDescription
user-agentChrome UA stringDefault user agent string
referer—Default HTTP referer
header—Default HTTP headers (array)
load-cookies—Path to a Netscape/Mozilla format cookies.txt (also accepts curl/wget output) loaded into the per-task cookie jar
cookie—Per-task cookie string sent as a Cookie header
checksum—Per-task whole-file checksum <algo>:<hex> (sha-256, sha-1, or md5; aria2's checksum= prefix is accepted), verified on completion
netrc-path~/.netrc (%USERPROFILE%\_netrc on Windows)Override the .netrc file used to inject HTTP Basic auth when a URL has no embedded credentials
no-netrcfalseSkip .netrc lookup entirely

Proxy

See the Proxy Support guide for URL schemes, scopes, and bypass syntax.

KeyDefaultDescription
all-proxy—HTTP-profile proxy server URL derived from proxy.http. Accepts http://, socks5://, or socks5h://. Used for HTTP/HTTPS, Metalink, HLS, yt-dlp, FTP/FTPS/SFTP, and Usenet when the Download scope is selected
no-proxy—Bypass list for all-proxy, one entry per line (comma-separated also accepted). Accepts hosts (example.com, matching subdomains too), host:port, IP literals, CIDR ranges (10.0.0.0/8), and * to bypass everything. Matching is case-insensitive; localhost, *.localhost, and loopback addresses are always bypassed. Unparseable entries are dropped
p2p-proxy—P2P TCP proxy URL derived from proxy.p2p. Same schemes as all-proxy. Used for BitTorrent TCP peers and HTTP tracker announces, eD2K TCP, Gnutella, G2, and giFT
p2p-no-proxy—Bypass list for p2p-proxy (same syntax as no-proxy)
p2p-udp-proxy—Optional P2P UDP proxy URL. When empty, UDP traffic reuses p2p-proxy. SOCKS5/SOCKS5H is required for DHT, UDP trackers, µTP, and Kad; an HTTP proxy cannot carry UDP
p2p-udp-no-proxy—Bypass list for p2p-udp-proxy
proxynested objectDesktop/UI preference. Canonical shape is { http: { enable, server, bypass, scope }, p2p: { enable, server, bypass, udp: { server, bypass } } }. The engine reads the flattened keys above

DNS over HTTPS

When enabled, hostnames are resolved by POSTing RFC 8484 wire-format queries (application/dns-message) to a DoH endpoint instead of using the system resolver. A and AAAA records are queried together and the answers cached in memory (smallest record TTL, clamped to 30–3600 s, plus a 5 s negative cache).

This applies to every HTTP client in the engine — downloads, m3u8 segments, RSS, WebDAV/S3 uploads, BitTorrent HTTP trackers, and UPnP. It does not cover BitTorrent UDP trackers/DHT or FTP/SFTP/ED2K/ADC connections, which still resolve through the system. SOCKS5h proxies keep resolving names proxy-side.

KeyDefaultDescription
doh-enablefalseRoute DNS lookups over HTTPS. Only takes effect when doh-url is also set
doh-url—DoH endpoint URL. Must be https:// (e.g. https://cloudflare-dns.com/dns-query)
doh-bootstrap—Comma/space/newline-separated IPs used to reach the endpoint host without a system-DNS lookup. Entries that don't parse as an IP are dropped. When empty, the endpoint host is resolved via the system resolver
doh-fallbacktrueWhen a DoH query fails (timeout, network error, SERVFAIL), fall back to the system resolver instead of failing the connection. An NXDOMAIN or NODATA answer is final and does not fall back
doh-providercloudflareUI-only preference (user.json). The desktop app's provider dropdown (cloudflare, google, quad9, custom); selecting a preset fills doh-url and doh-bootstrap automatically. Not read by the engine

DoH settings are hot-reloaded — changing any doh-* key takes effect on the next connection without an engine restart.

Reliability

KeyDefaultDescription
connect-timeout60Connect timeout in seconds for HTTP/HTTPS downloads and NZB URL fetches. FTP/SFTP do not read it
nzb-body-timeout30Maximum idle time in seconds between chunks while fetching an NZB URL body; applies only to URL-based NZB ingestion
lowest-speed-limit0Stalled-transfer threshold in bytes/s. If the EMA stays below this for lowest-speed-limit-timeout, the worker is aborted (and restarted when auto-retry is enabled). 0 disables the watchdog
lowest-speed-limit-timeout30How many seconds the transfer must stay below lowest-speed-limit before it is considered stalled
auto-retryfalseDesktop app (user.json): automatically retry errored tasks
auto-retry-interval5Desktop app: base retry delay in seconds (1–300)
auto-retry-strategystaticDesktop app: static (fixed delay) or exponential
uri-selectorfeedbackMirror selection strategy when a task has multiple URIs: feedback (drop slow/failing mirrors), inorder (try left-to-right), or adaptive (prefer fastest)

Low-Speed Auto-Recovery

A separate watchdog from lowest-speed-limit. The renderer's EngineClient samples active task speeds, and any task whose downloadSpeed stays under low-speed-threshold for 5 consecutive polling ticks is force-paused and resumed (with a 30-second per-task cooldown between recovery cycles).

KeyDefaultDescription
auto-detect-low-speed-tasksfalseMaster switch. When false, the recovery loop never runs
low-speed-threshold20Speed threshold in KB/s below which a task counts toward the strike count

Pause/resume only revives a single stalled TCP connection — useful for HTTP/HTTPS, FTP/SFTP, and HTTP-segment streams (m3u8, media/yt-dlp). Peer-swarm protocols (BitTorrent, ED2K, ADC, Gnutella, G2, giFT) are explicitly excluded from this recovery: pause tears down peer connections, aborts in-flight tracker announces and choke negotiations, and the swarm spends minutes warming up again. Tasks whose kind is not in {http, ftp, media, m3u8}, or whose kind is missing, are skipped.

Storage

KeyDefaultDescription
file-allocationfalloc (none on Android)Disk reservation strategy. falloc reserves real blocks via fallocate(2) (Linux) or fcntl(F_PREALLOCATE) (macOS), falling back to set_len. trunc only sets the file length. none lets writes grow the file as needed. The aria2 alias prealloc is accepted as a synonym for falloc
auto-file-renamingtrueWhen the target output file already exists, rename to name.1.ext, name.2.ext, … instead of overwriting
remote-timefalseStamp the local file mtime from the server's Last-Modified header on completion (per-task)
use-remote-file-time—UI/global preference mirrored into per-task remote-time when set
m3u8-output-formattsContainer produced after concatenating HLS segments (ts keeps the original transport stream; other values trigger a remux)
file-category-dirs—Per-category default directory overrides (object keyed by category id, e.g. { "video": "/downloads/video" }); applied as a fallback when no routing rule matches

BitTorrent

KeyDefaultDescription
keep-seedingfalseSeed until manually stopped (overrides seed-time / seed-ratio)
seed-ratio0Stop seeding at this ratio. 0 disables this goal; if both goals are 0 and keep-seeding is off, torrents stop at completion
seed-time0Seed for this many minutes after completing. 0 disables this goal
bt-tracker—Additional tracker URLs (comma- or newline-separated)
select-file—Per-task: 1-based indices of the files to download, with aria2-style ranges such as 1-3,7. Padding files are not counted. Applied live via changeOption. See file selection
bt-max-peers-per-torrent100Max concurrent peer connections per torrent (engine restart)
bt-max-outstanding-per-peer0Per-peer pipelined chunk-request cap (engine restart); 0 uses adaptive control starting at 6 and growing up to 500, while an explicit value is capped at 500
bt-max-connections400Total peer connections plus pending dials across all torrents (integer 20-5000; Preferences -> Advanced -> BitTorrent -> Max Peer Connections). Applied live, no engine restart. The effective value is capped at half the process file-descriptor soft limit, which the app raises at startup (to 8192 where the OS allows), so 400 is normally effective. Lowering it never drops open connections; it only holds back new ones. A small reserve (8 slots or 2%, at most a quarter of the cap) is kept for magnet metadata fetches, which count against the budget and wait for a slot instead of failing. Synced in the bittorrent category
bt-upload-rate-limit0BitTorrent upload rate cap in bytes/s (0 = unlimited); accepts size suffixes such as 512K. The effective cap is the smaller non-zero of this and max-overall-upload-limit. Applied live
bt-ban-corrupt-peerstrueBan peers that send data failing piece hash checks (engine restart). false disables bans
bt-ban-corrupt-strikes3Failed pieces attributed to one peer before its IP is banned for the rest of the session in every torrent (engine restart; the app UI accepts 1-100). Only used while bt-ban-corrupt-peers is on
bt-enable-upnptrueMap the listen port on the router via UPnP IGD (engine restart)
bt-upnp-lease300UPnP mapping lease in seconds (engine restart)
bt-enable-lsdtrueBEP-14 Local Service Discovery via multicast (engine restart)
bt-encryption-policypreferMSE/PE policy: plaintext, prefer, or require (engine restart)
bt-listen-v6falseAlso bind an IPv6 TCP listener on the same port (engine restart)
ed2k-port4662eD2K TCP listen port (engine restart)
ed2k-serverbuilt-in listComma-separated eD2K servers (host:port)
ed2k-enable-kadtrueEnable client-only eMule Kad source discovery over UDP (engine restart)
ed2k-kad-port4672Local Kad UDP port; must be in 1..=65535 (engine restart)
bt-create-subfoldertrueWhen true, multi-file torrents and magnets are placed under <dir>/<name>/. When false, files are written directly into dir. Single-file torrents are unaffected. Read on each task add (no restart needed); accepted as a per-task option override
bt-save-metadatatrueWhen true and a magnet link is added, the engine resolves the torrent metadata first and writes a synthesized .torrent file to the task's dir before starting the download. Has no effect when adding a .torrent file directly

The DHT (BEP-5) is always enabled and runs as a dual-stack client when an IPv6 socket can be bound. The BitTorrent TCP listener binds listen-port (21301 by default; for a range or list such as 6881-6889 the first port is used), falling back to a random port if it cannot be bound. dht-listen-port is not bound by anything yet (the DHT shares the listener's UDP port); it is only validated by the health check. bt-force-encryption maps to bt-encryption-policy require when the policy is unset or prefer; an explicit plaintext or require policy wins. The legacy aria2 keys enable-dht, enable-dht6, enable-peer-exchange, bt-enable-lpd (use bt-enable-lsd), bt-exclude-tracker, follow-torrent, allow-overwrite, and continue have been removed from the engine defaults because nothing read them. They are still accepted if a client or saved config sends them, but have no effect, and continue and follow-torrent are no longer synced. bt-load-saved-metadata (default true) makes a magnet reuse the .torrent saved by bt-save-metadata instead of resolving its metadata over the network again.

RPC

KeyDefaultDescription
rpc-listen-port16800RPC server port
rpc-host127.0.0.1RPC bind address (set to 0.0.0.0 for LAN access); a user.json key. Also used by the PeerBanHelper listener when that API is enabled
rpc-secretgenerated on first runRPC authentication token. On first run the engine generates a random 256-bit hex secret and saves it in system.json with an rpc-secret-initialized marker; a secret you later clear on purpose stays cleared. See RPC authentication
pbh-enablefalseStart a second JSON-RPC listener in Aria2Next compatibility mode so PeerBanHelper can monitor and ban BitTorrent peers (engine restart)
pbh-listen-port16801PeerBanHelper RPC port (engine restart). In PeerBanHelper, add an Aria2Next downloader pointed at http://127.0.0.1:<port>/jsonrpc
pbh-rpc-secret—Token required as token:<secret> on the PeerBanHelper listener (engine restart)

Media (yt-dlp)

KeyDefaultDescription
media-formatyt-dlp defaultyt-dlp --format selector (e.g. bestvideo+bestaudio/best, mp4). Aliases: youtube-format, yt-format
media-merge-formatmp4Container passed to --merge-output-format when ffmpeg is available. Alias: youtube-merge-format

RSS

See the RSS Feeds guide for the full workflow. RSS subscriptions, items, and rules are persisted via the desktop app's storage. Each feed is polled on its own interval (default 1800 s, minimum 60 s).

The old rss-auto-update and rss-update-interval keys are no longer used or listed by the app.

Task Routing

See the Task Routing guide.

KeyDefaultDescription
task-routing-rules[]Array of { id, label, pattern, dir, enabled } rules. The first enabled rule whose case-insensitive glob pattern matches the inferred filename wins; dir overrides the download directory and label is exposed as the task tag. Falls through to file-category-dirs then global dir

Completion Scripts

Run a script after every download finishes (success or failure). Per-task overrides are accepted via the desktop app's task options. See the Completion Scripts guide.

KeyDefaultDescription
completion-script-enabledfalseMaster switch. When false, no script runs even if command is set
completion-script-command—Absolute path to the executable to invoke (no shell parsing)
completion-script-args—Whitespace-tokenised arguments template. Supported placeholders inside any token: {path} (final file path), {hash} (BitTorrent info hash; empty for non-torrent tasks), {status} (complete / error). The same values are also exported as RISUKO_PATH, RISUKO_HASH, RISUKO_STATUS env vars
completion-script-timeout-ms30000Per-invocation timeout in milliseconds. Clamped to 1000–300000 (5 minutes). stdout/stderr captured into the log are truncated to 8 KiB

Usenet / NZB

See the Usenet and NZB Downloads guide for the provider setup and recovery workflow. These settings are used for new NZB tasks; task options snapshot the selected profile reference, cleanup policy, and any confirmed archive-limit override.

KeyDefaultDescription
usenet-profiles[]Non-secret provider profile metadata. Each profile has an ID, name, host, port, security mode, enabled state, priority, connection cap, plain-NNTP opt-in, and update timestamp. Usernames and passwords are kept in the OS keychain.
usenet-archive-limitsPlatform defaultsTask-wide archive and PAR2 safety limits. Desktop defaults: 500,000 entries, 2 TiB total expansion, 512 GiB per entry, depth 16, 1000:1 ratio, 10 GiB reserve, 6 active hours. Android defaults: 100,000, 256 GiB, 64 GiB, depth 16, 1000:1, 2 GiB, 2 active hours.
usenet-cleanup-modekeep-allSuccessful cleanup policy: keep-all, delete-par2, or delete-par2-and-volumes. delete-par2-and-archives remains accepted as a legacy alias.
usenet-limits-adjustedfalseSet when a synchronized archive-limit value was invalid or had to be reduced for this device.

Provider profiles use camel-case fields inside the usenet-profiles array:

[
  {
    "id": "primary",
    "name": "Primary provider",
    "host": "news.example.net",
    "port": 563,
    "securityMode": "implicit-tls",
    "enabled": true,
    "priority": 0,
    "maxConnections": 4,
    "allowPlain": false,
    "updatedAt": 1765000000000
  }
]

Valid securityMode values are implicit-tls, starttls, and plain. Plain NNTP also needs allowPlain: true; the app requires a separate user acknowledgement before it saves that state. Lower priority values run first.

The following option keys may be set through risuko.addNzb. The current Add Task form uses the global Usenet archive policy and does not expose per-task archive controls yet:

KeyDescription
usenet-profile-idRestrict the task to one configured provider profile. Omit it to use the enabled provider pool.
usenet-cleanup-modeOverride the global cleanup policy for this task.
usenet-archive-limitsObject with any of maxEntries, maxExpandedBytes, maxEntryBytes, maxNestingDepth, maxCompressionRatio, freeSpaceReserveBytes, and maxActiveSeconds.
usenet-archive-limit-override-confirmedRequired when a per-task override raises an enforced safety limit above its platform default.

Task options must not include a username, password, archive password, or credential-bearing provider object. Risuko removes those values before it saves the task or returns it through RPC.

Saved Credentials

KeyDefaultDescription
saved-credentials[]Non-secret credential profile metadata (id, label, scope, username). Passwords and SSH private keys are stored separately in the OS keychain (risuko-credentials service) and never written to user.json

Legacy P2P / IPC stacks

KeyDefaultDescription
gift-enabledfalseAllow gift:// URIs to be forwarded to a local giftd daemon. Shown in Preferences -> Advanced next to the legacy protocol toggles; once enabled and a giftd is reachable, gift:// links work
gift-host127.0.0.1giFT daemon host (also in Preferences -> Advanced)
gift-port1213giFT IPC port (also in Preferences -> Advanced)

The old adc-hub, adc-nick, gnutella-cache, and g2-cache keys are no longer listed by the app and nothing reads them. Gnutella and G2 downloads connect directly to the peer in the URI, and ADC / DC transfers are not implemented.

External Engine

By default the desktop app boots an embedded engine in-process. Switch to External Engine mode to point the UI at a separately running risuko serve (or any aria2-compatible RPC server). When enabled, the embedded engine is not started; all add_uri, polling, and config writes are routed over JSON-RPC.

KeyDefaultDescription
external-engine-enabledfalseSkip the embedded engine and connect to a remote RPC server instead
external-engine-host—Hostname or IP of the remote RPC server
external-engine-port16800Port of the remote RPC server
external-engine-secret—Token to send as token:<secret> on each call

Logging

KeyDefaultDescription
log-dir-override""Override the directory where rolling log files are written. Must be an absolute path to a writable directory. When empty (the default), logs go to the OS-specific app log directory. Useful on Android to redirect logs to a publicly accessible path
log-levelwarnVerbosity of the tracing subscriber. One of error, warn, info, debug, trace (these are tracing_subscriber::EnvFilter directives). Takes effect only after an app restart, because the subscriber is initialised once at startup

Startup-only Keys

The following keys are read once when the engine starts. Changing them at runtime is accepted (the value is persisted) but does not take effect until the engine is restarted (Quit & relaunch the desktop app, or restart risuko serve):

dht-listen-port, ed2k-enable-kad, ed2k-kad-port, ed2k-port, listen-port, rpc-host, rpc-listen-port, rpc-secret, pbh-enable, pbh-listen-port, pbh-rpc-secret, bt-max-peers-per-torrent, bt-max-outstanding-per-peer, bt-ban-corrupt-peers, bt-ban-corrupt-strikes, bt-enable-upnp, bt-upnp-lease, bt-enable-lsd, bt-encryption-policy, bt-listen-v6.

All other keys are hot-reloaded on the next task or polling cycle.

On this page