Skip to content

About

Self-hosted Stremio addon. Simply query your Jackett and stream torrents instantly via WebTorrent.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

jackstream

Docker Pulls GHCR CI License: MIT Go

Self-hosted Stremio addon — query your Jackett and stream torrents instantly via an embedded BitTorrent client. Static Go binary, ~20 MB Docker image, no debrid, no qBittorrent, no volumes.

Quick start (homelab on LAN)

  1. On your homelab/server:
    docker compose up -d
  2. From any device on the same LAN, open http://<server-ip>:7000/configure.
  3. Fill in your Jackett URL + API key, your TMDB API key. For Addon public URL just enter your server's LAN IP (e.g. 192.168.0.15) — the page auto-converts it to an HTTPS URL (https://192-168-0-15.local-ip.medicmobile.org:7001) so Stremio clients on other devices can reach it with a valid TLS cert.
  4. Click Install in Stremio — the native Stremio app opens and installs the addon.
  5. Pick a movie or episode — Jackett torrents appear as sources.

No domain, no cert management, no per-device trust setup. HTTPS uses a wildcard cert from local-ip.medicmobile.org (see HTTPS details for how it works and its tradeoffs).

Architecture

Stremio app
    │ GET /{config}/manifest.json
    │ GET /{config}/stream/{type}/{id}.json
    ▼
[Go net/http + Stremio addon]
    │
    ├─ Resolve IMDB id → titles via TMDB (24 h cache)
    ├─ Search Jackett in parallel across title variants
    ├─ Parse + sort torrents (quality > source > hdr > seeders)
    └─ Return streams pointing back to /stream/{config}/{torrentId}/{fileIdx}

    │ GET /stream/:config/:torrentId/:fileIdx
    ▼
[anacrolix/torrent singleton]
    │
    ├─ Download .torrent via Jackett (with passkeys)
    ├─ Reader-driven piece prioritization (responsive + 16 MB readahead) —
    │  only the parts you actually watch are downloaded
    ├─ Wait for torrent metadata, then serve via http.ServeContent (Range-aware)
    └─ Clean up idle torrents + their on-disk pieces, respect maxConcurrentTorrents

Features

  • 🪶 Static Go binary — ~20 MB Docker image on scratch base, <50 ms startup, ~30 MB RSS
  • 🧲 Direct streaming from Jackett results, no debrid, no qBittorrent
  • 🔒 Private-tracker friendly: uses Jackett's proxied .torrent URLs with passkeys; DHT / LSD / µTP disabled
  • 🔐 HTTPS out of the box via a self-refreshing wildcard cert — no reverse proxy, no rebuilds when it rotates
  • ⚙️ Per-user credentials encoded in the addon URL (zero server-side storage)
  • 💾 Client-side config persistence (localStorage) + bookmarkable /<config>/configure URL
  • 🎬 Movies + TV series (anime covered via category 5070)
  • 🌍 TMDB-powered title resolution with localized + original-title variants for better search coverage
  • 🏷️ Quality / source / HDR / codec / audio detection
  • 🎚️ Advanced filters: preferred language, quality floor/ceiling, size bounds, blacklist keywords
  • 📦 Single Docker container, no volumes, multi-arch (amd64 + arm64)

Prerequisites

Installation

Docker (one-liner)

docker run -d --name jackstream \
  -p 7000:7000 -p 7001:7001 \
  --restart unless-stopped \
  ghcr.io/lborruto/jackstream:latest

Docker Compose

services:
  jackstream:
    image: ghcr.io/lborruto/jackstream:latest
    container_name: jackstream
    ports:
      - "7000:7000"   # HTTP (configure page + localhost Stremio)
      - "7001:7001"   # HTTPS (for Stremio clients on other LAN devices)
    restart: unless-stopped

From source (development)

git clone https://github.com/lborruto/jackstream.git
cd jackstream
go build -o jackstream ./cmd/jackstream
./jackstream

Requires Go 1.24+. All deps are stdlib except github.com/anacrolix/torrent (fetched automatically on build).

Configuration

  1. Open http://<server-ip>:7000/configure from any device on your LAN.
  2. Fill in:
    • Jackett URL (e.g. http://<server-ip>:9117) and its API key.
    • TMDB API key.
    • Addon public URL / LAN IP — just type your server's LAN IP (e.g. 192.168.0.15). The page auto-converts it to a valid HTTPS URL (see HTTPS details).
  3. Click Test Jackett / Test TMDB to verify connectivity (live ping of both).
  4. (Optional) expand Advanced filters to set language preference, quality bounds, size limits, or blacklist keywords.
  5. Click Install in Stremio — the native Stremio app opens and installs.
  6. The HTTPS URL is also shown so you can paste it into Stremio manually on any device (useful for TVs without a browser).

Your inputs are saved to localStorage so reloading /configure re-populates the form. You can also bookmark http://<server-ip>:7000/<base64>/configure to carry the full config between browsers.

Advanced filters

All optional. Defaults produce the same behavior as earlier versions.

Setting Effect
Preferred audio language Sort boost for matching audio (FRENCH / MULTI / VOSTFR / ENG). FRENCH also boosts MULTI releases; same for ENG. Does not hide non-matching results.
Min / Max quality Hard filter. Drops results above/below the chosen tier.
Min size (MB) Drops "fake" low-size releases.
Max size (GB) Drops oversized releases (useful for slow connections / small disks).
Blacklist keywords Comma-separated list (case-insensitive, word-boundary). Drops results whose title matches any keyword — e.g. CAM, HDCAM, TELESYNC.

All filters are stored in the base64-encoded addon URL, so different Stremio installs can use different filter profiles against the same backend.

HTTPS details

jackstream uses the wildcard TLS cert from https://local-ip.medicmobile.org — a community service that issues a public *.local-ip.medicmobile.org Let's Encrypt certificate and operates a DNS server where <dashed-ip>.local-ip.medicmobile.org resolves to <ip>. This gives you valid HTTPS for any LAN IP with zero setup.

The cert is refreshed automatically at runtime: on startup and every 12 h the addon re-fetches the current cert + key from the service and hot-swaps it into the running HTTPS listener — no restart, no image rebuild when Let's Encrypt rotates it (~every 60 days). An embedded copy is baked into the binary as a fallback so HTTPS still comes up when the service is briefly unreachable.

The HTTPS listener binds on port 7001 alongside the HTTP listener on 7000. Stremio clients hit the HTTPS URL; the TCP connection still goes directly to your LAN host.

Tradeoffs

  • The TLS private key is public (by design — anyone can download it from the service). Anyone who can also control DNS on your network could MITM; on a trusted home LAN this is a non-issue. Not suitable for addons exposed to the public internet.
  • The service is community-run (Medic Mobile). If it goes offline, the last successfully fetched cert keeps serving until it expires (a warning is logged from 14 days out); after that, switch to your own cert (below).

Using your own cert instead

Mount a cert + key and point the addon at them (this pins your cert — auto-refresh is disabled and renewal is up to you):

services:
  jackstream:
    image: ghcr.io/lborruto/jackstream:latest
    ports:
      - "7000:7000"
      - "7001:7001"
    environment:
      - HTTPS_CERT_PATH=/certs/fullchain.pem
      - HTTPS_KEY_PATH=/certs/privkey.pem
    volumes:
      - /path/to/your/certs:/certs:ro
    restart: unless-stopped

Works with Caddy-issued certs, mkcert, Let's Encrypt, or anything else.

Disabling HTTPS

Set HTTPS_DISABLED=1 to skip the HTTPS listener (HTTP-only). Useful if you front the addon with Caddy / Nginx / Traefik that already terminates TLS.

Refreshing the embedded fallback cert

Rotation is handled at runtime, so this is not needed for running installs. The embedded copy only matters for the first seconds after a cold start while the fresh cert is fetched — refreshing it occasionally before a release keeps that window valid:

./scripts/refresh-certs.sh
git commit -am "chore: refresh embedded local-ip fallback cert"

FAQ

Does it work with private trackers? Yes — Jackett proxies the .torrent URL so announce URLs carry passkeys. DHT and LSD are disabled in the BitTorrent client to avoid leaking infohashes to the public swarm.

Why did I see "Stream session expired"? The in-memory torrent map has a 2 h TTL. Go back to the source list and click the stream again — it's instant.

Does it seed after I'm done watching? No — V1 stops idle torrents after TORRENT_IDLE_TIMEOUT_MIN (default 30 min) and frees the disk. Seeding is on the V2 roadmap.

Why not debrid? Out of scope. The spec targets homelab-only streaming with zero third-party dependencies.

Why an embedded BitTorrent client instead of qBittorrent? No shared volume, no second container, native HTTP Range support, and the Jackett-proxied .torrent carries the passkey so private trackers work out of the box.

Stremio desktop vs TV? Both work. notWebReady: true tells Stremio to use the native player rather than the web player.

The Install in Stremio button did nothing / the URL lost the :7001 port. Some Stremio clients (older Windows builds in particular) mishandle the port in stremio:// deep-link URIs. Workaround: copy the https://…:7001/<base64>/manifest.json URL shown below the button, then in Stremio click the puzzle-piece icon → paste into Add-on Repository URL.

Loading failed when I click a stream. Most often because the container restarted (e.g. Komodo redeploy) and the in-memory torrentStore was wiped while Stremio still had a cached list of stream URLs. Close and re-open the movie in Stremio to force a fresh stream-list fetch.

Environment variables

Variable Default Meaning
PORT 7000 Port to listen on
DATA_DIR /tmp/jackstream Where in-flight torrent pieces are stored (auto-cleaned)
CACHE_TTL_MINUTES 1440 TMDB cache TTL
REQUEST_TIMEOUT_MS 8000 Jackett/TMDB timeout
STREAM_READY_TIMEOUT_S 60 Max wait for first pieces
TORRENT_IDLE_TIMEOUT_MIN 30 Idle minutes before stopping a torrent and deleting its pieces
HTTPS_PORT 7001 Port for the HTTPS listener
HTTPS_CERT_PATH (unset) Path to your own TLS certificate (PEM). Disables cert auto-refresh
HTTPS_KEY_PATH (unset) Path to your own TLS private key (PEM). Both paths must be set
HTTPS_DISABLED (unset) Set to 1 to disable the HTTPS listener

Changelog

v1.2.0 — Self-refreshing HTTPS + leaner downloads (2026-08-14)

  • 🔐 TLS cert now auto-refreshes at runtime. The addon fetches the current local-ip.medicmobile.org cert on startup and every 12 h and hot-swaps it into the HTTPS listener — no more image rebuild every ~60 days. The embedded cert is now only a cold-start fallback; a warning is logged if refresh keeps failing within 14 days of expiry. HTTPS_CERT_PATH/HTTPS_KEY_PATH still pin your own cert (auto-refresh off).
  • 📉 Streams no longer download the whole file in the background. Piece prioritization is now driven purely by playback position (+16 MB readahead), so skipping through or abandoning a stream no longer pulls the full release to disk. Less bandwidth, less disk, kinder to private-tracker ratios.
  • 💾 DATA_DIR env var to choose where in-flight pieces are stored (default /tmp/jackstream).
  • 🏷️ Detected codec (x264 / x265 / AV1) now shown in the stream description.
  • 🧹 Internal polish: fixed a data race on the max-concurrent-torrents setting, expired stream-session entries are now reclaimed even when never re-read, torrent-file fetches are time- and size-bounded, disk deletion no longer blocks stream requests, and dead code was removed.

No breaking changes. Existing addon install URLs continue to work unchanged.

v1.1.1 — Disk cleanup (2026-04-23)

  • 🗑️ Fix: bt.Cleanup() and Shutdown() now delete downloaded pieces from disk, not just from memory. Previously the container's writable layer (/tmp/jackstream) grew unboundedly until docker rm. Long-running containers now reclaim disk as torrents go idle or at shutdown.
  • Internal: dropAndDelete() helper replaces direct torrent.Drop() calls; Shutdown() additionally wipes the DataDir to catch orphan files from fetch-fail paths.

No user-facing behavior change. Existing installs continue to work; upgrade at any time.

v1.1.0 — Go rewrite (2026-04-23)

Full rewrite from Node.js to Go with strict behavioral parity. Same URLs, same config format, same UX, but:

  • 🪶 93% smaller Docker image (~280 MB → ~20 MB on scratch base)
  • 🚀 <50 ms startup vs ~250 ms Node
  • 💾 ~30 MB RSS vs ~100–150 MB
  • 🛡️ No more utp-native segfaults — anacrolix/torrent is pure Go, µTP disabled by default
  • 🎯 HTTP Range streaming via http.ServeContent replaces ~245 lines of manual Range handling

No user-facing changes. Existing addon install URLs continue to work unchanged.

v1.0.0 — Initial release (2026-04-22)

  • Node.js 20 + stremio-addon-sdk + webtorrent
  • HTTP + HTTPS listeners (ports 7000/7001) with bundled local-ip.medicmobile.org cert
  • Configure page with localStorage persistence + advanced filters

Contributing

PRs welcome. Please run go test ./... and go vet ./... before opening one.

License

MIT — see LICENSE.

About

Self-hosted Stremio addon. Simply query your Jackett and stream torrents instantly via WebTorrent.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages