The daemon can now stream a session straight from a server-resolved debrid
direct URL instead of disk/torrent, delivering the "play instantáneo
cache-fast" promise for cache-confirmed torrents the user never downloaded.
- debridFileProvider: an io.ReadSeekCloser over HTTP Range — network-free
Seek, lazy GET on Read, reopen-on-seek, a HEAD up front for the size, and
a URL-derived name so the served Content-Type is video/mp4 (not
octet-stream) when the web's name lacks an extension.
- OnStreamSession branches on StreamSession.DirectURL before the filePath
checks (no local path, no ffmpeg), builds the provider in a goroutine
(HEAD off the sync loop) and marks the session ready.
- Bump 0.10.0 -> 0.11.0 as the debrid-stream floor the web gates on.
Validated e2e against a real AllDebrid account: a cached mp4 plays 1080p in
Chrome through the agent, including the high-offset seek for a non-faststart
file's moov atom. 2b (HLS-from-URL for mkv/HEVC) + 2c (cache-fast preference
+ mid-stream fallback) remain.
/stream and /hls were served with no auth (only CORS + rate limit), so a
funnel- or UPnP-exposed daemon leaked active downloads to anyone with the URL.
Bind a short-lived HMAC token (scope + 6h expiry) to every stream URL the
daemon hands out and verify it on each request:
- /stream + VLC playlist: ?t= query, agent-minted, scope "stream"
- /hls: path segment /hls/<session>/<token>/<resource>, web-minted with the
agent's reported secret, scope "hls:<session>" — relative playlist URIs
inherit it with no rewriting
- NO loopback exemption: cloudflared relays public funnel traffic over
localhost, so a loopback source address is not a trust signal
- the agent reports its per-run signing key on register only when enforcing
- require_stream_token config (default true); secret fails hard if rand fails
- /playlist.m3u no longer self-mints a token (was an open token oracle)
Roadmap: Docs/plans/unarr-agent-roadmap.md (hueco #1).
Deploy the web HLS-minting change BEFORE shipping this agent release.
Phase 3 security audit follow-up. Medium and low-severity hardenings
plus a deferred-work plan for the cross-repo stream-token rollout.
Stream server CORS: replace the wildcard Access-Control-Allow-Origin
with an allowlist that echoes back only torrentclaw.com,
app.torrentclaw.com, the local Next dev port (3030 — matches the web
repo package.json) and any extras the operator adds via the new
downloads.cors_extra_origins TOML key. A Vary: Origin header is now
emitted whenever the request carries an Origin header so an
intermediate cache cannot serve a stale ACAO to a different origin.
URL scheme guard: openBrowser and OpenPlayer refuse any URL that is
not http(s). Combined with passing the URL after "--" wherever the
launched helper supports it (open, mpv, vlc, cvlc), this stops a
leading "-" from being parsed as a switch by the spawned process.
State file permissions: WriteState now writes 0o600 so the agent ID,
PID and counters cannot be enumerated by another local user on a
shared host. Matches the existing config file mode.
ZIP slip defense-in-depth: extractZip extracts the safety check into
safeZipPath, which canonicalises the entry name (normalising
backslashes to "/"), rejects "..", "../" prefix and "/../" interior
components, and verifies the final destination stays inside destDir
before opening any file.
Mirror fallback: documented the design for multi-provider
mirrors.json hosting in the comment block on DefaultStaticFallbackURLs
and added a follow-up note about signing it with the same ed25519
release key. The list is kept at one provider until the second host
is provisioned and added to torrentclaw-web's STATIC_FALLBACKS.
Deferred work: a new plan document Docs/plans/security-stream-token.md
covers the per-task stream token (Phase 2.2 of the original audit)
which requires coordinated web + CLI work and ships separately.
- Mirror pool with health tracking and exponential backoff for failed hosts
- Agent client routes requests through mirror pool with retry semantics
- New `unarr mirrors` command to inspect mirror state and force failover
- `unarr status` now detects 401 from /agent/register and suggests `unarr login`
instead of the generic "Could not fetch account info" message
- Config supports multiple ScanPaths for upcoming multi-path library scan
- Draft plan for bidirectional library sync (CLI ↔ Web) under Docs/plans/