Configuration
Risuko configuration reference — all available options.
Risuko stores configuration in two JSON files in the config directory:
system.json— System-level defaults (created on first run)user.json— User overrides
User settings take precedence over system defaults.
Config Directory Locations
| Environment | Path |
|---|---|
| macOS | ~/Library/Application Support/dev.risuko.app |
| Linux | ~/.config/dev.risuko.app |
| Windows | %APPDATA%\dev.risuko.app |
| Desktop App | Managed by Tauri (platform-specific app data) |
| Node.js API | Configurable via EngineConfig.configDir |
Reading Configuration
CLI
# List all settings
risuko config list
# Get a specific value
risuko config get dir
# JSON output
risuko config list --jsonNode.js
import { getGlobalOption } from "@risuko/risuko-js";
const options = await getGlobalOption();
console.log(options.dir);
console.log(options["max-concurrent-downloads"]);Writing Configuration
CLI
risuko config set dir '"/home/user/downloads"'
risuko config set max-concurrent-downloads '"5"'Values must be valid JSON. Strings need double quotes inside quotes: '"value"'
Node.js
import { changeGlobalOption } from "@risuko/risuko-js";
await changeGlobalOption({
dir: "/home/user/downloads",
"max-concurrent-downloads": "5",
});Option Reference
General
| Key | Default | Description |
|---|---|---|
dir | OS default | Default download directory |
max-concurrent-downloads | 5 | Maximum 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 | true | Resume interrupted downloads |
purge-record-on-start | false | When 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.
| Key | Default | Description |
|---|---|---|
auto-check-update | false | Automatically check the Tauri updater endpoint for signed release updates. Desktop-only and opt-in on every platform |
last-check-update-time | 0 | Last update-check timestamp recorded by the app |
legal-accepted | false | First-launch Terms/Privacy acceptance flag; device-local and excluded from cloud sync |
clipboard-watch | true on desktop | Watch the clipboard for download links and show a prompt. Not created on Android |
clipboard-watch-extensions | archive, installer, media, document, .torrent, meta4, metalink extensions | HTTP(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-seen | false on desktop | Device-local flag for the one-time clipboard-watch notice |
Connection
| Key | Default | Description |
|---|---|---|
split | 16 | Connections per download (HTTP/HTTPS); ignored when the file is smaller than min-split-size or the server doesn't support Range |
min-split-size | 1M | Minimum segment size before the file is split (bytes; accepts K/M/G suffixes) |
Speed Limiting
| Key | Default | Description |
|---|---|---|
max-overall-download-limit | 0 | Global download speed limit (bytes/s, 0 = unlimited) |
max-overall-upload-limit | 0 | Legacy aria2 key. Accepted and persisted but not currently enforced by the engine; the only working upload cap is BitTorrent seeding via bt-upload-rate-limit |
max-download-limit | 0 | Per-task download speed limit (bytes/s, applies to HTTP, m3u8, and FTP workers) |
HTTP
| Key | Default | Description |
|---|---|---|
user-agent | Chrome UA string | Default 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 |
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-netrc | false | Skip .netrc lookup entirely |
Proxy
See the Proxy Support guide for URL schemes, scopes, and bypass syntax.
| Key | Default | Description |
|---|---|---|
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 |
proxy | nested object | Desktop/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.
| Key | Default | Description |
|---|---|---|
doh-enable | false | Route 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-fallback | true | When a DoH query fails (timeout, network error, SERVFAIL), fall back to the system resolver instead of failing the connection |
doh-provider | cloudflare | UI-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
| Key | Default | Description |
|---|---|---|
connect-timeout | 60 | Connect timeout in seconds for HTTP/HTTPS/FTP |
nzb-body-timeout | 30 | Maximum idle time in seconds between chunks while fetching an NZB URL body; applies only to URL-based NZB ingestion |
lowest-speed-limit | 0 | Stalled-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-timeout | 30 | How many seconds the transfer must stay below lowest-speed-limit before it is considered stalled |
uri-selector | feedback | Mirror 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).
| Key | Default | Description |
|---|---|---|
auto-detect-low-speed-tasks | false | Master switch. When false, the recovery loop never runs |
low-speed-threshold | 20 | Speed 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
| Key | Default | Description |
|---|---|---|
file-allocation | falloc (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-renaming | true | When the target output file already exists, append .1, .2, … instead of overwriting |
remote-time | false | Stamp 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-format | ts | Container 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
| Key | Default | Description |
|---|---|---|
keep-seeding | false | Seed until manually stopped (overrides seed-time / seed-ratio) |
seed-ratio | 0 | Stop seeding at this ratio (0 = no ratio limit) |
seed-time | 0 | Seed for this many minutes after completing (0 = no time limit) |
bt-tracker | — | Additional tracker URLs (comma-separated) |
bt-max-peers-per-torrent | 100 | Max concurrent peer connections per torrent (engine restart) |
bt-max-outstanding-per-peer | 0 | Per-peer pipelined chunk-request cap (engine restart); 0 uses adaptive control starting at 6 and growing up to 96, while an explicit value is capped at 256 |
bt-upload-rate-limit | 0 | Seeding upload rate cap in bytes/s (0 = unlimited) (engine restart) |
bt-enable-upnp | true | Map the listen port on the router via UPnP IGD (engine restart) |
bt-upnp-lease | 300 | UPnP mapping lease in seconds (engine restart) |
bt-enable-lsd | true | BEP-14 Local Service Discovery via multicast (engine restart) |
bt-encryption-policy | prefer | MSE/PE policy: plaintext, prefer, or require (engine restart) |
bt-listen-v6 | false | Also bind an IPv6 TCP listener on the same port (engine restart) |
ed2k-enable-kad | true | Enable client-only eMule Kad source discovery over UDP (engine restart) |
ed2k-kad-port | 4672 | Local Kad UDP port; must be in 1..=65535 (engine restart) |
bt-create-subfolder | true | When 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-metadata | true | When 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 listener uses an ephemeral port; the
legacy aria2 keys listen-port, dht-listen-port, enable-dht,
enable-dht6, enable-peer-exchange, and bt-enable-lpd are accepted for
backwards compatibility but currently have no effect.
RPC
| Key | Default | Description |
|---|---|---|
rpc-listen-port | 16800 | RPC server port |
rpc-host | 127.0.0.1 | RPC bind address (set to 0.0.0.0 for LAN access). Also used by the PeerBanHelper listener when that API is enabled |
rpc-secret | — | RPC authentication token |
pbh-enable | false | Start a second JSON-RPC listener in Aria2Next compatibility mode so PeerBanHelper can monitor and ban BitTorrent peers (engine restart) |
pbh-listen-port | 16801 | PeerBanHelper 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) |
YouTube (yt-dlp)
| Key | Default | Description |
|---|---|---|
youtube-format | yt-dlp default | yt-dlp -f format selector (e.g. bestvideo+bestaudio/best, mp4) |
RSS
See the RSS Feeds guide for the full workflow. RSS subscriptions, items, and rules are persisted via the desktop app's storage; the keys below are user-level UI preferences.
| Key | Default | Description |
|---|---|---|
rss-auto-update | — | Enable periodic feed polling (boolean; controlled from the RSS panel) |
rss-update-interval | — | Polling interval in minutes between automatic refreshes |
Task Routing
See the Task Routing guide.
| Key | Default | Description |
|---|---|---|
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.
| Key | Default | Description |
|---|---|---|
completion-script-enabled | false | Master 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-ms | 30000 | Per-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.
| Key | Default | Description |
|---|---|---|
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-limits | Platform defaults | Task-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-mode | keep-all | Successful cleanup policy: keep-all, delete-par2, or delete-par2-and-volumes. delete-par2-and-archives remains accepted as a legacy alias. |
usenet-limits-adjusted | false | Set 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:
| Key | Description |
|---|---|
usenet-profile-id | Restrict the task to one configured provider profile. Omit it to use the enabled provider pool. |
usenet-cleanup-mode | Override the global cleanup policy for this task. |
usenet-archive-limits | Object with any of maxEntries, maxExpandedBytes, maxEntryBytes, maxNestingDepth, maxCompressionRatio, freeSpaceReserveBytes, and maxActiveSeconds. |
usenet-archive-limit-override-confirmed | Required 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
| Key | Default | Description |
|---|---|---|
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
| Key | Default | Description |
|---|---|---|
adc-hub | — | Default ADC / DC hub URI (adc(s)://, dchub://, nmdc://) used as a hint when a file URI doesn't include one |
adc-nick | — | Nickname presented to the ADC / DC hub |
gnutella-cache | — | GWebCache URLs (comma-separated) used to bootstrap Gnutella ultrapeers |
g2-cache | — | Bootstrap caches for the Gnutella2 (G2) network |
gift-enabled | false | Allow gift:// URIs to be forwarded to a local giftd daemon |
gift-host | 127.0.0.1 | giFT IPC bind address |
gift-port | 1213 | giFT IPC port |
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.
| Key | Default | Description |
|---|---|---|
external-engine-enabled | false | Skip the embedded engine and connect to a remote RPC server instead |
external-engine-host | — | Hostname or IP of the remote RPC server |
external-engine-port | 16800 | Port of the remote RPC server |
external-engine-secret | — | Token to send as token:<secret> on each call |
Logging
| Key | Default | Description |
|---|---|---|
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-level | warn | Verbosity 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-listen-port,
rpc-secret, pbh-enable, pbh-listen-port, pbh-rpc-secret,
bt-max-peers-per-torrent, bt-max-outstanding-per-peer,
bt-upload-rate-limit, 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.