Risuko
Architecture

Protocol Handlers

How Risuko handles different download protocols.

Risuko's engine includes specialized handlers for each supported protocol. Each handler is a separate module within risuko-engine.

HTTP/HTTPS

Module: engine/http.rs

The HTTP handler supports multi-threaded range-based downloads:

  • Sends a GET request with Range: bytes=0-0 to probe file size and range support
  • Divides the file into pieces (1 MiB for small files, scaling up to 16 MiB with the file size) when it is larger than min-split-size × split; spawns N worker tasks (configurable via split, like aria2's -s/-x) that pull pieces from a shared lock-free queue
  • Work-stealing is inherent: any idle worker claims the next free piece, so a slow connection never holds back the rest of the download
  • Resume granularity is per-piece (at most one piece lost on SIGKILL): each piece tracks an AtomicU32 byte counter that the writer increments per flushed Bytes, and a sidecar JSON snapshot is persisted every 2 s
  • Downloads use HTTP Range headers; multi-connection downloads use one HTTP/1.1 connection per segment, so workers never collapse onto a single HTTP/2 connection
  • Each piece streams bytes::Bytes directly into a dedicated writer task that issues positioned writes (pwrite on Unix, seek_write on Windows) against a shared file handle, so workers never contend on a seek lock
  • Calls fsync once on completion before renaming the .part file into place
  • Validates the Content-Range header on every 206 response and aborts on mismatch; sends If-Match when an ETag was recorded for resume. Multi-piece resume validates with the strong ETag, or with Last-Modified when the server sends none
  • Falls back to a single-connection download when the file is below min-split-size or the server doesn't support ranges
  • Handles redirects, cookies, and custom headers. A resolved redirect target is reused for later pieces; credentials and user-supplied headers are dropped on cross-origin hops
  • Single-connection downloads request identity encoding and store the bytes exactly as served (no transparent gzip decoding, so .gz files stay compressed)
  • Stalled header or body reads hit the idle timeout (timeout, default 60 s)
  • Metalink files download several files at once, bounded by max-concurrent-downloads
  • Supports proxy connections via the all-proxy option (http://, socks5://, socks5h://; https:// proxies are not supported). This is the HTTP profile, distinct from the P2P profile
  • Optional DNS over HTTPS: when doh-enable + doh-url are set, hostnames resolve via RFC 8484 wire-format queries to the endpoint instead of the system resolver. Applied process-wide to every risuko-http client through a pluggable resolver hook, with an in-memory TTL cache and optional fallback to system DNS (doh-fallback)
  • Stalled-transfer watchdog: aborts a worker when the EMA stays below lowest-speed-limit for lowest-speed-limit-timeout (auto-retry restarts it when enabled)
  • Multi-URI tasks pick a mirror via uri-selector (feedback / inorder / adaptive)
  • Optional .netrc lookup for HTTP Basic auth when the URL has no embedded credentials (disabled with no-netrc)
  • Cookies can be primed from a Netscape/Mozilla cookies.txt via load-cookies

Key Options

OptionDefaultDescription
split16Number of parallel worker tasks (one TCP connection each)
min-split-size1MSkip splitting for files smaller than this
connect-timeout60Connect timeout in seconds
lowest-speed-limit0Stalled-transfer floor in bytes/s (0 disables the watchdog)
lowest-speed-limit-timeout30Seconds below the floor before the worker is aborted
uri-selectorfeedbackMirror selection strategy: feedback, inorder, or adaptive
file-allocationfalloc (none on Android)Disk reservation strategy: falloc, trunc, or none
user-agentMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36Custom user agent
header—Custom HTTP headers
all-proxy—Proxy URL (http://, socks5://, or socks5h://)
doh-enablefalseResolve DNS over HTTPS (needs doh-url); global, not per-task
doh-url—DoH endpoint URL (https:// only)
doh-bootstrap—IPs for reaching the endpoint without system DNS
doh-fallbacktrueFall back to system DNS when a DoH query fails (timeout, network error, SERVFAIL); NXDOMAIN/NODATA answers are final
load-cookies—Path to a Netscape/Mozilla cookies.txt
netrc-path~/.netrcOverride the .netrc file location
no-netrcfalseSkip .netrc lookup
referer—HTTP referer
max-download-limit0Per-task speed limit (bytes/s, 0 = unlimited)
remote-timefalseSet the local file mtime from the server's Last-Modified header

BitTorrent

Module: engine/torrent.rs

Built on risuko-bt:

  • Magnet link resolution (session fast-path for already-managed torrents, then tracker + DHT). Accepts both xt=urn:btih: (v1) and xt=urn:btmh: (v2 multihash) URNs; hybrid magnets carry both. x.pe peers are dialled directly and BEP-53 so= seeds the file selection
  • File selection via aria2-style select-file (1-based, padding files not counted), changeable on a running torrent; bytes of deselected files that share a piece with selected ones go to part files under .risuko-parts/<info-hash>/
  • .torrent file parsing via the bundled bencode decoder; supports v1, pure-v2, and hybrid metainfo (BEP 52 meta version=2 with file tree and piece layers)
  • BEP-19 WebSeeds from top-level url-list metadata: HTTP(S) mirrors are used to fetch missing pieces with validated byte ranges and piece hashes, alongside peer transfers. Multi-file torrents require a base URL ending in /; private torrents do not use WebSeeds
  • BEP-5 DHT for peer discovery, dual-stack (IPv4 + IPv6 via BEP-32) when an IPv6 socket can be bound; per-torrent continuous get_peers polling and announce_peer keep the peer supply live throughout the download session. Pure-v2 swarms announce the leading 20 bytes of the SHA-256 info-hash on the v1 DHT/trackers per BEP 52
  • BEP-14 Local Service Discovery (LSD) for peers on the same LAN
  • BEP-10 extension protocol with ut_metadata (BEP-9) and full BEP-11 PEX (ut_pex): received peer-exchange messages are parsed and gossiped peers are fed into the dial channel
  • BEP-55 ut_holepunch NAT traversal: advertised in the extended handshake. When a direct dial to a PEX-gossiped peer fails, Risuko asks the relay that gossiped it to perform a rendezvous; as a relay it forwards connect messages so two NATed peers can simultaneously open a connection
  • BEP-29 µTP (Micro Transport Protocol) with LEDBAT congestion control, selective ACKs, Nagle-style write coalescing, and path-MTU discovery via don't-fragment probes: outbound dials attempt TCP first and fall back to µTP when the TCP dial or handshake fails (peers reached by hole punching are dialled over µTP first); inbound µTP connections are accepted on the same UDP port as the TCP listener
  • Message Stream Encryption (MSE/PE) over both TCP and µTP with selectable plaintext / prefer / require policy
  • BEP-8 tracker peer obfuscation: HTTP(S) trackers from obfuscate-announce-list are announced with sha_ih and an obscured port, their RC4-encrypted peer lists are decrypted, and those peers are dialled with MSE first
  • BEP-52 BitTorrent v2: SHA-256 Merkle piece-tree verification (16 KiB block leaves, BEP 52 pad hashes), reserved-bit 0x10 in handshake byte 7 advertises v2 capability, and the HashRequest / Hashes / HashReject wire messages exchange Merkle layer slices with sibling proofs, both requested and served (piece layers and, for verified pieces, block-level layers)
  • BEP-47 padding files are hidden from file lists and never written to disk
  • UPnP IGD port forwarding for the TCP listener and the µTP UDP listener (IPv6 is not mapped). A mapping that belongs to another LAN host is never deleted or taken over; the conflict (error 718) is reported instead
  • Rarest-first piece selection with endgame mode; SHA-1 (v1) or SHA-256 Merkle root (v2) verification off the async runtime
  • HTTP/HTTPS (BEP-23 compact) and UDP (BEP-15) trackers. Public torrents announce to every tracker in every tier concurrently, so user and extra trackers are on equal footing; private torrents keep BEP-12 per-tier failover. Also BEP-7 per-family IPv4/IPv6 announces merged into one peer list and BEP-41 URL data (path and query, e.g. passkeys) on UDP announces
  • Configurable seeding (ratio, time, or keep-seeding), upload speed limiting. Seed time is counted from when seeding resumes after a restart. A finished torrent whose seeding goal is reached (or any torrent when keep-seeding is off) is stopped in the engine rather than left seeding invisibly
  • The resume hash check runs in the background: pause and stop stay responsive, and nothing unverified is served or announced until it finishes
  • Peers that send corrupt data are struck only when attribution is certain (sole contributor of a failed piece, or blocks differing from the later verified copy); at bt-ban-corrupt-strikes the IP is banned for the session in every torrent (the oldest of up to 4096 bans is evicted). WebSeeds are never struck
  • Sends NotInterested when a peer has nothing left for us and advertises upload_only (BEP-21) once complete
  • WebSeeds back off on transient errors (429/5xx, network errors) and only disable a mirror, or a single file on it, on permanent errors
  • Repeated disk write failures stop the torrent with an error until it is resumed
  • Tunable per-peer request pipeline and per-torrent peer cap. All torrents also share a session-wide budget of live peer connections plus pending dials (bt-max-connections, default 400, applied live, never above half the open-file soft limit, which the app raises at startup); lowering it only holds back new connections, and a small reserve is kept for magnet metadata fetches, which wait for a slot instead of failing. Running out of file descriptors is treated as back-pressure rather than a failed dial
  • On engine shutdown every torrent is stopped in parallel through the normal stop path, bounded to 10 s overall, so trackers receive stopped
  • Optional P2P proxy (p2p-proxy / p2p-udp-proxy) for TCP peers, HTTP/UDP tracker announces, DHT, and µTP; SOCKS5/SOCKS5H is required for UDP. LSD and UPnP stay on the local network. See the Proxy Support guide
  • Process-wide IP/CIDR peer blocklist via risuko.setBtPeerBlocklist (used by the PeerBanHelper Aria2Next listener)

Private torrents (BEP-27) are isolated from public discovery. Risuko disables their DHT lookup and announce, LSD, PEX, hole punching, and WebSeed requests; only tracker-sourced peers are authorized for the swarm. Inbound peers are accepted when their IP was returned by a tracker.

Pure-v2 magnet links resolve by fetching the BEP 52 piece layers from peers over HASH_REQUEST after the info dict. If no peer serves those layers, resolution fails with piece layers unavailable. Hybrid magnets fall back to the v1 info dict when layers are missing but a btih hash is present.

Key Options

OptionDescription
seed-ratioStop seeding after this upload/download ratio
seed-timeStop seeding after this many minutes
keep-seedingSeed until manually stopped (overrides seed-time / seed-ratio)
bt-upload-rate-limitUpload speed limit (bytes/s, 0 = unlimited, 512K-style values accepted). The effective cap is the smaller non-zero of this and max-overall-upload-limit. Torrents also honour max-overall-download-limit and the per-task max-download-limit / max-upload-limit; all are applied live
bt-trackerAdditional tracker URLs
select-fileFiles to download, aria2 syntax such as 1-3,7 (per task; see File Selection)
bt-max-peers-per-torrentMax concurrent peer connections per torrent (default 100)
bt-max-outstanding-per-peerPer-peer pipelined chunk-request cap; 0 uses adaptive control starting at 6 and growing up to 500, while an explicit value is capped at 500
bt-max-connectionsSession-wide cap on peer connections plus pending dials (20-5000, default 400, applied live)
bt-ban-corrupt-peersBan peers whose data fails piece hash checks (default true)
bt-ban-corrupt-strikesAttributed failures before a peer's IP is banned (default 3)
bt-enable-upnpUPnP IGD port forwarding (default true)
bt-upnp-leaseUPnP mapping lease in seconds (default 300)
bt-enable-lsdBEP-14 Local Service Discovery (default true)
bt-encryption-policyMSE/PE policy: plaintext, prefer, require (default prefer)
bt-listen-v6Also bind an IPv6 TCP listener (default false)
listen-portTCP listener port (default 21301; first port of a range or list), random port if it cannot be bound
bt-force-encryptionMaps to require when bt-encryption-policy is unset or prefer
p2p-proxySOCKS5/HTTP proxy for BitTorrent TCP peers and HTTP tracker announces
p2p-udp-proxyOptional SOCKS5/SOCKS5H proxy for DHT, UDP trackers, and µTP; empty reuses p2p-proxy

Seeding Behavior

After a torrent download completes, seeding behavior depends on the seed options:

  1. onBtDownloadComplete fires when the download finishes
  2. If keep-seeding is true, or seed-time > 0, or seed-ratio > 0, the task stays active and seeding continues
  3. When keep-seeding is true, seed-time / seed-ratio limits are ignored; seeding runs until the task is stopped manually
  4. Otherwise seeding stops when seed-time elapses or seed-ratio is reached (whichever comes first)
  5. onDownloadComplete fires when seeding is done (or immediately if no seeding was requested)

Usenet / NZB

Modules: engine/usenet.rs, engine/usenet_worker.rs, and engine/usenet_transport.rs

The Usenet handler creates one usenet task for each parsed NZB manifest. It validates the XML file list and segment numbers, fetches articles through the configured NNTP provider pool, decodes yEnc, assembles multipart output, and uses in-process PAR2 verification and repair when possible. The task stores a non-secret manifest and durable resume receipts; provider passwords remain in the platform keychain.

Provider profiles carry one NNTP endpoint. The pool tries lower numeric priorities first, rotates equal-priority profiles, retains a healthy warm connection, opens multiple parallel connections per provider up to its cap, and fails over on recoverable connection or article failures. Each connection pipelines up to 3 ARTICLE requests, which helps on high-latency servers, and a missing article fails only that article. Implicit TLS and STARTTLS use normal hostname and certificate validation. Plain NNTP requires an explicit per-profile opt-in.

Archive safety limits apply across yEnc assembly and PAR2 processing. The extraction primitives reject unsafe paths, links, special files, traversal, archive-bomb limits, and finite resource limits. The worker does not currently invoke archive extraction, so archive volumes remain until a verified extraction stage is available.

See the Usenet and NZB Downloads guide for provider setup, PAR2 recovery, cleanup, and platform limits.

The nzb-body-timeout option limits the idle time between chunks when an NZB manifest is fetched from an HTTP(S) URL; it defaults to 30 seconds and does not affect local .nzb files or article downloads from NNTP providers. NNTP article fetches follow the HTTP proxy profile (all-proxy / no-proxy) when the Download scope is enabled.

ED2K

Module: engine/ed2k/

Downloads files by known hash using ed2k://|file|… links (server links are rejected). Sources come from OP_GET_SOURCES on the configured servers, any sources, entries in the link, and the client-only eMule Kad source-discovery service when enabled; only high-ID sources are used. Kad uses UDP for source discovery and does not provide inbound ED2K transfer, keyword search, sharing/upload, or encryption. Server, peer, and Kad sockets follow the P2P proxy profile (p2p-proxy / p2p-udp-proxy). Downloads are written to <name>.part with a <name>.part.met sidecar and resume after pause, crash, or retry; only MD4-verified parts are restored, and the final file appears only after the whole file verifies. Removing the task, including removing it from history, deletes both partial files. Files over 4 GiB are supported with eMule-compatible peers that advertise large-file support; other peers are skipped for such files. Sources that place us in their upload queue are kept and re-asked about every 29-31 minutes instead of being dropped, and a download whose sources are all queued keeps waiting instead of failing with "no sources". Kad accepts compressed (0xE5) replies, and lookups use a sliding window that streams sources as soon as close nodes answer.

Key Options

OptionDefaultDescription
ed2k-server176.123.5.89:4725, 45.82.80.155:5687, 85.239.33.123:4232, 91.208.162.87:4232, 145.239.2.134:4661Comma-separated ip:port server list. Ships seeded with the five servers shown; falls back to a built-in list only if set empty
ed2k-port4662Local client TCP 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)

M3U8/HLS

Module: engine/m3u8/

Downloads HTTP Live Streaming content:

  • Parses master and media M3U8 playlists (highest-bandwidth variant selected from a master playlist)
  • Downloads segments concurrently (bounded by split, default 16) with per-segment retry and a resume-progress sidecar; the segment temp dir (.m3u8_<name>_<gid>_<hash>) is stable per task and playlist, so resume works across restarts, and it is removed when the task is removed. In-progress downloads from older versions are migrated automatically
  • .netrc credentials are sent to the task's host only, never to other hosts. Playlists are capped at 4 MiB and keys at 1 KiB
  • Fetches EXT-X-MAP init segments, honours the task's headers, referer, user agent, and cookies on playlist, key, and segment requests, and applies timeouts
  • Decrypts AES-128-CBC segments (EXT-X-KEY); SAMPLE-AES and other methods are written through undecrypted
  • Concatenates segments into a single .ts, optionally remuxing to MP4 via ffmpeg when m3u8-output-format is mp4
  • VOD only — live playlists (no #EXT-X-ENDLIST) are rejected

FTP/SFTP

Module: engine/ftp/

File Transfer Protocol handler:

  • FTP with optional implicit-TLS encryption (FTPS)
  • SFTP (SSH File Transfer Protocol) via russh + russh-sftp
  • Server host keys pinned on first use (TOFU) via engine/ssh_known_hosts.rs; subsequent fingerprint mismatches are refused. The same store is shared with the SFTP upload sink so trust state stays consistent across download and upload paths
  • Resume from an existing .part via FTP REST or SFTP positional reads (SFTP reads are pipelined and respect the server's read limit; RSA keys sign with rsa-sha2); single file per task (no directory listing or multi-file transfer)
  • FTPS certificate verification on by default (check-certificate); SFTP auth tries the PEM private key (sftp-private-key) first and falls back to the password, the same as SFTP uploads; a second FTP/SFTP task writing the same .part path is refused
  • Optional HTTP-profile proxy via all-proxy / no-proxy (local loopback bridge for the FTP control/data connection)

Media (yt-dlp)

Module: engine/media.rs

Delegates extraction and download to an external yt-dlp process resolved from PATH (not bundled; check_yt_dlp_available runs yt-dlp --version first). Routing uses engine::media::is_media_uri, an allowlist of ~26 host suffixes (YouTube, Vimeo, Twitch, Bilibili, SoundCloud, TikTok, and others); the force-ytdlp option forces any URL through yt-dlp. The handler:

  • Probes the URL with yt-dlp --dump-json --no-playlist --flat-playlist to fetch a JSON info object (title, duration, formats), exposed via the Tauri command get_media_info
  • Spawns yt-dlp with --newline --progress-template and parses progress lines into the same atomic counters used by HTTP/BT, so the regular speed/progress UI works
  • Picks the format selector from the media-format option (falling back to youtube-format, then yt-format); merges via ffmpeg when it is on PATH
  • Watches yt-dlp's before_dl / after_move / Destination: lines to learn the resolved output filename
  • Forwards proxy, user-agent, referer, and cookies (all-proxy/proxy, user-agent, referer, cookies file or cookie header). The forwarded proxy is the HTTP profile, not the P2P profile
  • For YouTube URLs adds --extractor-args youtube:player_client=web,default

Routed automatically by add_uri whenever is_media_uri matches; the dedicated add_media Tauri command and risuko.addMedia RPC method bypass detection.

ADC / Direct Connect (NMDC + ADC dialects)

Module: engine/adc/

Schemes: adc://, adcs:// (parsed as a TLS flag), dchub://, nmdc://. Default port 411 (ADC), 412 (ADCS). Direct file URIs of the form dchub://hub.host[:port]/?TTH=<base32>&xl=<bytes>&dn=<name> carry the TTH hash, file size and display name.

The transfer implementation (NMDC $Lock/$Key/$ADCGET, the ADC framed protocol, and TTH/Tiger hashing) was removed. The handler now only parses the URI: it validates the scheme, requires a TTH and non-zero xl, stores the size, then returns no source has the requested file without any network I/O. Risuko runs passive-only with no listening socket, so $ConnectToMe / ADC CTM can never complete. adcs:// TLS is not implemented.

ADC / Direct Connect (both NMDC and ADC dialects) parses URIs but cannot download. No handshake, search, transfer, or encryption exists in the current build.

Gnutella 0.6

Module: engine/gnutella/

URI scheme: gnutella://host[:port]/uri-res/N2R?urn:sha1:<base32> (also gnet://). Default port 6346. The Gnutella wire protocol itself is not implemented — there is no GNUTELLA CONNECT/0.6 handshake, no GWebCache discovery, and no PING/QUERY/QUERYHIT. The handler:

  • Connects directly to the host in the URI and issues an HTTP/1.1 GET /uri-res/N2R?<urn> with an X-Gnutella-Content-URN header (User-Agent: Risuko/0.1). The connection uses the P2P proxy profile when configured
  • Accepts only 200 or 206, requires a Content-Length (no chunked encoding), and checks it is within 4 KiB of the xl= size hint
  • Streams the single source to disk with no range resume and no SHA-1 verification (the URN is only a request key)
  • Removing the task deletes its .part file

Gnutella2 (G2)

Module: engine/g2/

URI scheme: g2://host[:port]/sha1/<base32>?xl=<bytes>&dn=<name>. Default port 6346. No G2 wire protocol is implemented; run_g2_download reuses the Gnutella fetch_by_urn path with a hardcoded /uri-res/N2R route since both networks serve content over the same peer-to-peer HTTP/1.1 endpoint. Same single-source fetch, length tolerance, and lack of hash verification as Gnutella. Removing a Gnutella or G2 task deletes its .part file.

giFT IPC bridge

Module: engine/gift/

URI scheme: gift://<inner-uri>. Off by default. When gift-enabled is true, the handler connects to a locally-running giftd daemon (default 127.0.0.1:1213, override with gift-host / gift-port) over its line-oriented IPC socket and forwards the inner URI verbatim. Status is parsed from TRANSFER STATUS id=… total=… done=… state=active|complete|... lines so progress bars work like any other transfer. Cancelling the task issues a TRANSFER REMOVE id=… to giftd.

The legacy P2P stacks (ADC/Direct Connect, Gnutella, G2, giFT) are best-effort and depend on the public network still being reachable. They share the engine's progress/cancel/auto-retry plumbing but bypass the multi-threaded HTTP downloader. ADC/Direct Connect currently only parses URIs and cannot transfer; giFT is off unless a local giftd is running and gift-enabled is set.

RSS

Module: engine/rss/

Automatic feed subscription and download:

  • Add RSS/Atom feed URLs
  • Periodic polling for new items (configurable interval)
  • Automatic download of new items matching configured rules
  • Persistent feed state via StorageBackend

The RSS manager uses tokio::spawn for async polling and requires a tokio runtime context.

Cloud Upload Sinks

Module: engine/upload/

Push completed downloads to a remote destination. The upload subsystem mirrors the RssManager pattern: a single UploadSinkManager owns sinks, routing rules, and a bounded job queue. Each protocol implements an UploadSink trait (upload + test) and is dispatched from manager::build_sink_runtime.

FileRole
sink.rsUploadSink trait, SinkConfig discriminated union, UploadFile / UploadControl / UploadProgress
manager.rsUploadSinkManager: CRUD, persistence, semaphore-bounded job queue, event emission
rules.rsUploadRule matcher (categories, extensions, size bounds, task kinds)
webdav.rsWebDAV: single PUT, MKCOL for missing parents, optional Basic auth, optional self-signed-cert mode
s3.rsS3-compatible: SigV4 with UNSIGNED-PAYLOAD. Single PUT up to 5 GiB, then 3-step multipart pipeline (POST ?uploads= → PUT ?partNumber&uploadId → POST ?uploadId) with a 64 MiB default part size and best-effort DELETE ?uploadId to abort orphaned uploads. Virtual-host or path-style URLs
sftp.rsSFTP via russh + russh-sftp, password or PEM private-key auth. Server host keys are pinned on first use (TOFU) via the shared ssh_known_hosts store and refused on mismatch
ftp.rsFTP / implicit-TLS FTPS via suppaftp, optional self-signed-cert mode (insecure)

Sink selection is per-task override → first matching rule → default sink id. The manager runs uploads on a tokio::Semaphore (default permits = 2, clamped to 1..=16) and emits engine:upload-{queued,start,complete,error,cancelled} events. Persisted state lives under the upload-sinks key via StorageBackend.

See the Cloud Upload Sinks guide for user-facing configuration.

Protocol Routing

The Tauri add_uri command and the desktop Add Task modal sniff the URI scheme to dispatch transparently. The raw JSON-RPC layer keeps protocol-specific methods so that aria2-style clients still know exactly what they're calling:

ProtocolURI scheme(s)RPC MethodNode.js FunctionCLI
HTTP/HTTPShttp(s)://risuko.addUriaddUririsuko download <url>
BitTorrent (.torrent)— (file path)risuko.addTorrentaddTorrentrisuko download <file.torrent> (auto-detected)
BitTorrent (magnet)magnet:risuko.addUri (sniffed)addMagnetrisuko download <magnet>
Usenet / NZBlocal .nzb in the app, or an HTTP(S) URL ending in .nzbrisuko.addNzb (base64 data)Not availableNot available
ED2Ked2k://risuko.addEd2kaddEd2k—
M3U8/HLS*.m3u8, *.m3urisuko.addUri (sniffed)addM3u8—
FTP/SFTPftp(s)://, sftp://risuko.addUri (sniffed)addFtp—
Media (yt-dlp)media host allowlist (~26 sites)risuko.addMedia—risuko download <url>
ADC / Direct Connectadc(s)://, dchub://, nmdc://risuko.addUri (sniffed)——
Gnutella 0.6gnutella://, gnet://risuko.addUri (sniffed)——
Gnutella2 (G2)g2://risuko.addUri (sniffed)——
giFT IPCgift://risuko.addUri (sniffed)——

Magnet links are sniffed inside add_http_task, the shared target of risuko.addUri, so the CLI and aria2-style clients add magnets too — a magnet: URI is routed to the torrent handler wherever it enters. Scheme sniffing for M3U8, FTP, and the legacy P2P protocols still lives only in the Tauri add_uri command, so a .m3u8, ftp://, or gnutella:// URL sent over plain JSON-RPC is enqueued as an HTTP task. The CLI's download command distinguishes .torrent files (path ends with .torrent and exists on disk → risuko.addTorrent) and media URLs (is_media_uri or --ytdlp → risuko.addMedia); everything else, magnets included, is passed to risuko.addUri. The Node.js API also offers protocol-specific functions (addTorrent, addMagnet, addEd2k, addM3u8, addFtp) that call the engine directly without scheme sniffing.

On this page