Sign inSign up

irc2torrent/flood_rtorrent_irc2torrent

By irc2torrent

•Updated 26 days ago

IRC announce bot + rTorrent + Flood, hardened and shell-less, with the #443 disk-read fix

Buildkit cache
Image
Networking
0

793

irc2torrent/flood_rtorrent_irc2torrent repository overview

⁠flood_rtorrent_irc2torrent

An IRC announce bot, rTorrent and Flood in a single hardened container.

Announce channels are how a lot of trackers publish new releases in real time. Polling an RSS feed means waiting out the poll interval; sitting on the channel means acting the moment the line appears. irc2torrent connects, listens, filters against your regexes, fetches the .torrent, and hands it to rTorrent — usually within a second of the announce.

The runtime is built on Docker Hardened Images: no shell, no busybox, no package manager, running as uid 1000 with a read-only root filesystem.


⁠Why this image exists

rTorrent serves chunks via mmap(), and default kernel readahead pulls in far more than the piece being uploaded. A busy seedbox can read 4–100× more from disk than it sends (rakshasa/rtorrent#443⁠).

Upstream merged the fix, but both flags default to off, so upgrading rTorrent alone changes nothing. The bundled rtorrent.rc turns them on:

system.files.advise_random.set = 1     # kernel readahead off (posix_fadvise + madvise MADV_RANDOM)
pieces.preload.type.set        = 1     # rTorrent does its own readahead instead

system.files.advise_random.hashing is deliberately left off — hash checking still wants sequential readahead, and keeping that split is the entire point of the upstream change.

This tracks rakshasa/rtorrent 0.16.x, not the jesec fork, which has been dormant since July 2023 and is missing upstream's 2024–2026 thread-safety, socket and DNS rework — including this fix.


⁠Quick start

docker run -d --name rtorrent \
  --read-only --security-opt no-new-privileges:true \
  --tmpfs /tmp --stop-timeout 30 \
  -v rtorrent-config:/config \
  -v /srv/downloads:/downloads \
  -p 3000:3000 -p 50000:50000 -p 50000:50000/udp -p 50001:50001/udp \
  irc2torrent/flood_rtorrent_irc2torrent:latest

Open http://localhost:3000⁠, create the Flood account, and choose rTorrent → Unix socket → /config/.local/share/rtorrent/rtorrent.sock.

--stop-timeout 30 matters. Docker defaults to 10 seconds and then SIGKILLs everything, which costs rTorrent its session state.

On first run the bot writes default configs into /config/.config/ and exits. Edit them and start the container again.


⁠Configuration

Two files, both created with working defaults on first run: irc.toml for the connection, options.toml for everything else.

# Releases you want. A line is taken if it matches ANY of these...
regex_for_downloads_match = [
    "Some Release.*2160p.*",
    "Another Release.*S02.*1080p.*WEB.*"
]

# ...and dropped if it matches ANY of these. Reject wins over match.
regex_for_downloads_reject_match = [
    "(?i).*NORDIC.*",
    "(?i).*GERMAN.*"
]

# How to pull the release name and torrent id out of an announce line.
# The two named captures are required; the rest depends on your network.
regex_for_announce_match = '''.*Name:'(?P<name>.*)' uploaded by.*https://tracker.example.org/torrent/(?P<id>\d+)'''

# The tracker. The key is a label of your choosing and is what gets logged.
[platform.YourTracker]
download_url_template = "https://tracker.example.org/rss/download/{id}/{key}/{file}"
rss_key               = "XXXXXXXXXXXXXXXXXXXX"
torrent_dir           = "/downloads/.torrents"

[[clients]]

[clients.rTorrent]
xmlrpc_url = "unix:/config/.local/share/rtorrent/rtorrent.sock"

There is no tracker-specific code anywhere. A network is an announce regex plus a download_url_template — {id}, {name}, {file} and {key} are the placeholders. Adding a network is configuration, never code.

Three things worth knowing:

  • Matching is unanchored, so leading and trailing .* do nothing. "2160p" and ".*2160p.*" behave identically.
  • Use (?i) for case rather than listing variants. One (?i).*NORDIC.* covers NORDiC, Nordic and the rest.
  • regex_for_announce_match is the one you have to write yourself, since every network announces differently. Watch the channel by hand for a minute first.

{name} and {id} come verbatim from an IRC message, so every substituted value is percent-encoded and the host is re-checked before the request is made. A placeholder in the host or port is rejected at startup — otherwise a crafted release name could point the request, and your rss_key, at someone else's server.

Most of options.toml reloads live, no restart: the filters, the tracker block, and all notification settings. platform, clients, [telegram] and [slack] are read once at startup and log a line saying so rather than pretending to have applied.

⁠One slash after unix:, not two

unix:// opens a URL authority, so the first path segment is parsed as a hostname: unix://config/.local/… asks for /.local/…, and the only symptom is a connection error naming a path you never wrote.

The socket must live on a filesystem that supports socket inodes. Local storage is fine, including a bind mount to a NAS's own disks. A share re-mounted over SMB/CIFS cannot host one at all, and rTorrent fails to bind at startup rather than degrading quietly.


⁠Paths and ports

PathPurpose
/configAll persistent state — rTorrent session, Flood database, bot config. Back this up.
/config/.config/options.tomlFilters, tracker, client, notifications.
/config/.config/irc.tomlIRC connection settings.
/config/.local/share/rtorrent/.sessionrTorrent session.
/downloadsTorrent data.
PortPurpose
3000Flood web UI
50000 tcp+udpPeer traffic
50001/udpDHT

Change the peer port in both docker/rtorrent.rc and your run command together.


⁠Environment variables

Everything has a working default; you normally need to set none of them.

VariableDefaultPurpose
IRC2TORRENT_SUPERVISE1Run as container init. Unset to run the bot alone.
IRC2TORRENT_SHUTDOWN_GRACE8Seconds children get after SIGTERM before SIGKILL. Below docker stop's 10s default on purpose. Raise this and --stop-timeout together.
IRC2TORRENT_RAW_CHILD_LOGS0Child output is captured and prefixed [rtorrent] / [flood]. Set 1 for plain inheritance, keeping Flood's output as machine-parseable JSON.
FLOOD_OPTION_AUTH(unset)default or none. Set default if Flood is reachable beyond localhost.
FLOOD_OPTION_PORT3000Flood web UI port.
PUID / PGID1000Ownership of /config and /downloads.

Do not set FLOOD_OPTION_RTORRENT. It makes Flood spawn its own rTorrent, so you get two instances against one session directory — and Flood's copy does not load the bundled rtorrent.rc, so it runs without the disk-read fix.

⁠Remote syslog (new in 0.17.0)

Set IRC2TORRENT_SYSLOG and everything the bot logs goes to a collector as well as to docker logs — no shipper, no sidecar, no file to tail. Messages are RFC 3164, which QNAP QuLog Center, rsyslog, syslog-ng and Synology all accept.

-e IRC2TORRENT_SYSLOG=udp://192.168.1.10:514
-e IRC2TORRENT_SYSLOG_HOSTNAME=irc2torrent
VariableDefaultPurpose
IRC2TORRENT_SYSLOG(off)udp://host[:port], tcp://host[:port], unix, or unix:/path. Bare host[:port] means UDP. Port defaults to 514. IPv6 needs brackets.
IRC2TORRENT_SYSLOG_TAGirc2torrentProgram name in the header — what collectors group by.
IRC2TORRENT_SYSLOG_LEVELinfoThreshold for this sink alone.
IRC2TORRENT_SYSLOG_FACILITYdaemondaemon, user, local0–local7, …
IRC2TORRENT_SYSLOG_HOSTNAME(system hostname)Defaults to the short container id, which changes on every recreate — set it if you group by host.
IRC2TORRENT_SYSLOG_CHILD_LOGS0Also relay rTorrent and Flood output to the collector.

UDP is the default deliberately: the sink is synchronous, so a TCP target that stops answering can stall the bot. A target that cannot be opened is logged once and then ignored — never fatal.


⁠Notifications and IRC commands

Optional, both off by default.

Notifications go to email, ntfy, IRC private message, Telegram or Slack, on events like a torrent added, a download finished, disk running low, or a daily summary. Two settings keep an announce channel from becoming a firehose: digest_seconds (default 300) buffers events into one message with repeats collapsed into a count, and max_per_hour (default 20) is a hard ceiling that reports how many it suppressed. As of 0.17.0 a notification held for an absent owner expires after hold_seconds (default 15 minutes) instead of arriving as a weekend-long backlog of things that already resolved.

Commands over IRC let you query and control the bot from a private message. This exposes a control interface, so it is off by default. A nickname is not a credential — on a network without enforced registration anyone can take yours the moment you drop off — so require_identified (on by default) asks services who the sender is and refuses unless they are logged in to an account matching your configured name.


⁠How it fits together

One container, no s6-overlay. irc2torrent runs as PID 1, starts and reaps rTorrent and Flood, installs its own SIGTERM/SIGINT handlers, and waits for the SCGI socket before starting Flood — so Flood's first probe succeeds and it settles on JSON-RPC rather than the XML-RPC fallback.

Two independent RPC paths reach the same rTorrent: the bot uses XML-RPC over the SCGI unix socket, Flood prefers JSON-RPC over the same socket. Both dialects stay enabled.


⁠Security

irc2torrent parses untrusted input from a network it does not control, so it is built to be contained: non-root, read-only root filesystem, no shell or package manager in the runtime image, no-new-privileges.

A full security audit preceded 0.2.0 — a high-severity arbitrary file write reachable from a crafted IRC announce, three remotely triggerable panics, and dependency work that took cargo audit from 18 vulnerabilities to 0. All of it is fixed as of 0.2.0; run that or newer. cargo audit gates CI on every build.


⁠Tags

  • latest — the most recent release
  • 0.17.0, 0.16.0, … — pinned versions

⁠Scope

This ships no tracker configuration and no announce patterns for any particular network. What you connect to, and whether you are entitled to what you download, is yours to sort out.


Source, full documentation and issues: https://github.com/irc2torrent/irc2torrent⁠

Licensed under MIT. rTorrent and libtorrent are GPL-2.0, Flood is GPL-3.0; each retains its own license.

Tag summary

Content type

Image

Digest

sha256:3314ba609…

Size

101.8 MB

Last updated

26 days ago

docker pull irc2torrent/flood_rtorrent_irc2torrent