Rillio - private streaming in one place
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗Website: rillio.app
Rillio is a hard fork of the Stremio client, consolidated from five upstream Stremio repositories into a single monorepo. There is no upstream merge path: history was squashed and the repos were restructured. (The name comes from rill, a small quiet stream, for a calm, local-first app.)
Safety model in one line: torrent data is untrusted end to end - every file
path a torrent declares must resolve inside Rillio's own cache folder (a write
jail: absolute paths, drive prefixes, and .. traversal are rejected and the
offending torrent is removed), the local API accepts mutations only from the
app's own origin, and updates are signed. Details below.
This app category handles attacker-controlled input by definition: torrents, subtitles and addon responses all come from strangers. The hardening is aimed at attack classes that have actually shipped, with honest framing of how common each is:
.. traversal or absolute paths; libtorrent documented and fixed
this class in 2014, and
path-handling bugs keep recurring in clients (e.g. the 2025 Deluge advisories,
GHSL-2024-188..191).
Rillio validates every declared path against the cache root on top of
librqbit's own parse-time check, and removes the torrent on violation.None of this makes any software invulnerable, and this README will not claim otherwise. It means the known, shipped attack classes for this category each have a specific, auditable countermeasure here.
apps/
web/ React + TypeScript client (was Stremio/stremio-web)
desktop/src-tauri/ Tauri v2 desktop shell (Rust): owns the window, embeds the
streaming server in-process, and plays via native mpv
packages/
video/ Player abstraction layer (was Stremio/stremio-video)
translations/ Locale strings (was Stremio/stremio-translations)
crates/
core/ Rust state machines + addon transport (was Stremio/stremio-core)
core-web/ wasm-bindgen bridge, workspace package @rillio/core-web
derive/ Model derive macro
watched-bitfield/ Watched-episode bitfield
streaming-server/ Auditable Rust streaming/torrent server (replaces server.js)
landing/ Static marketing site (rillio.app)
Both a pnpm workspace (pnpm-workspace.yaml) and a cargo workspace
(Cargo.toml) are rooted here.
The desktop app is a Tauri v2 shell (apps/desktop/src-tauri) that:
apps/web/build) in a WebView2 window with its own chrome.127.0.0.1:11470 (no
container, no sidecar), replacing Stremio's closed-source server.js.stremio:// and rillio:// URL schemes, so a deep link (a
content link, or an addon-install link from the community directory) opens the
app and routes to the right screen.The web client is shared. In a plain browser it decodes with HTMLVideo (the
browser's decoder); in the desktop shell it uses ShellVideo, which drives the
native mpv. The same React app runs both places.
Player features beyond playback fidelity (as of v0.1.22):
crates/streaming-server is an auditable, in-process replacement for Stremio's
closed-source server.js. It uses librqbit for the torrent engine and serves
the same HTTP API, which was validated byte-for-byte against the reference
server.js during the port.
Its defaults are privacy-conscious, not an anonymity guarantee: it opens no inbound listen port and no UPnP by default, so it isn't left accepting inbound connections. BitTorrent still exposes your IP to peers, so bring your own SOCKS5/VPN if you need that hidden.
The loopback API is also guarded against other web origins: it checks the request Origin (rejecting arbitrary websites), keeps state-changing routes POST-only, and routes subtitle/proxy fetches through an SSRF guard, so a page you open in a browser cannot drive or read from your local server.
Torrent data is treated as untrusted at the filesystem level too: every file
path a torrent declares is validated to resolve under the cache root before
anything is streamed (absolute paths, drive prefixes, and .. traversal are
rejected), on top of librqbit-core's own parse-time path-traversal check, and a
torrent that fails the check is removed instead of written. A malicious
.torrent cannot plant files anywhere else on disk.
It is a separate cargo workspace because it pulls a large native tree (librqbit,
rustls, DHT) that needs url >= 2.5, which conflicts with the wasm crates'
url 2.4.* pin. Build it from its own directory.
Upstream, stremio-web consumed @stremio/stremio-core-web and
@stremio/stremio-video from the npm registry, so editing the Rust core did
nothing to the app. Here they are workspace:* links backed by local builds:
crates/core --(wasm-pack)--> crates/core-web --(workspace:*)--> apps/web
packages/video ------------------(workspace:*)------------------> apps/web
Editing crates/core/src changes the app after pnpm build:wasm. The upstream
Qt5 shell (stremio-shell) was dropped and replaced by the Tauri shell, so
packages/video/src/ShellVideo is reachable again and drives mpv instead of Qt.
| Tool | Version | Notes |
|---|---|---|
| Node | >= 22 | |
| pnpm | >= 11 | corepack prepare [email protected] --activate |
| Rust | 1.95 | pinned by rust-toolchain.toml |
| wasm-pack | >= 0.15 | cargo install wasm-pack --locked |
| libmpv | v2 | libmpv-2.dll beside the desktop exe, for playback only |
Web client:
pnpm install
pnpm build:wasm # cargo + wasm-pack -> crates/core-web
pnpm --filter rillio run build # webpack apps/web
Desktop app (Windows):
cd apps/desktop/src-tauri
cargo build --release # rillio-desktop.exe, embeds apps/web/build
Playback needs libmpv-2.dll beside the exe (or point RILLIO_LIBMPV at one).
When running a dev build, set RILLIO_STREAMING_CACHE_DIR to a throwaway
directory so it does not share (and evict torrents from) an installed app's cache.
Push a v* tag and .github/workflows/release.yml builds, signs, and publishes a
draft GitHub Release (installers + the updater manifest). Publish the draft to
ship it; existing installs update themselves.