Concepts - Kheopsian/Hydranos GitHub Wiki

🇬🇧 English · 🇫🇷 Français

Concepts and Glossary

Applies to Hydranos 4.4.

This page is the ten-minute mental model of Hydranos for someone who has never run it. Read it before the install pages if you want to know what you are installing. At the end you will know what an engine, a category, a node and a job are, where Hydranos keeps its state, and which word the rest of the wiki uses for each idea (the glossary at the bottom).

One program, several engines

An instance is one running hydranos process with its config file and its store. Everything happens inside that one process: the web UI, the APIs and every BitTorrent session. There is no separate engine process to start or supervise.

An engine is one BitTorrent session inside the process. Each engine has its own listen port (TCP, and uTP on the same port number), its own outgoing connections, its own DHT node (on unless enable_dht = false), its own announce scheduler, its own peer id and its own resume state. Its name is the engine id. Each engine also has its own way out: everything it sends (peer connections, announces, DHT, LSD) leaves by its interface, its managed WireGuard tunnel or its proxy, so two engines on two tunnels are two separate identities in the swarm (see VPN and Egress).

  • Every instance always has two engines, race and hoard, even if the config file has no [race] or [hoard] section.
  • You can add extra engines (for example vpn1) with engine blocks in the config file (TOML array of tables, see Configuration: Engines and Race Drain) or Nodes › This node › + Add an engine. Engines are built at boot, so a new engine only starts after a restart.
  • Each engine has a role, race or hoard. The role is a behaviour, not a name: an extra engine vpn1 with role = "race" behaves like the race engine (race announcing, race drain).

Details, defaults and extra-engine recipes: Engines: Race and Hoard. How the process is built: Architecture.

flowchart LR
    B["Browser (web UI)"] --> P
    A["Sonarr, Radarr, autobrr, cross-seed<br/>qBittorrent shim /api/v2"] --> P
    S["Scripts<br/>REST API /api"] --> P
    M["AI client<br/>MCP: POST /mcp"] --> P
    P["hydranos process<br/>port 8199, one API key"] --> R["engine race"]
    P --> H["engine hoard"]
    P --> X["extra engines (optional)"]
    R --> T["Trackers and peers"]
    H --> T
    X --> T
Loading

Race and hoard

The two built-in engines exist because torrents that come and go and torrents that stay want opposite things. Neither engine transfers faster than the other: peers, pieces and disk are the same code, and a race torrent downloads and uploads exactly as fast as a hoard torrent. What differs is everything around a torrent's life: how it announces, and whether it is meant to leave.

  • Race is the engine for churn: torrents grabbed the moment they are posted, raced in the swarm while it is young, then let go to make room for the next ones. It announces to every tracker of the torrent so it sits in every swarm, and the race drain frees its disks when they fill up: it deletes torrents whose tracker's seed obligation is met, and graduates the others if their category names a Graduate to category on another engine; a torrent with neither stays in place and is counted as stuck.
  • Hoard is for the torrents that stay: a library seeded for months, at any size. Its announces stop at the first tier of trackers that answers, which keeps announce traffic proportionate when you seed a very large number of torrents. The race drain never touches it. One sweep does delete hoard torrents with their data: the transit sweep, and only in a category marked Transit area that another category graduates into, once the seed obligation is met. A plain hoard category is never emptied automatically.

If unsure, file your categories as Hoard. Race is for racing fresh uploads, and the race drain can delete race torrents together with their data. Torrents imported from qBittorrent or Transmission always land in hoard, whatever their category's mode.

race role hoard role
Trackers announced on each pass every tracker of every tier stops at the first tier that answers
Torrent the tracker does not know yet ("unregistered") retried every 5 s for registration_retry_minutes (6 min by default) while it downloads normal schedule
Race drain (delete or graduate when a volume is full) yes no
Refuse an add when the volume lacks space ([race_drain] add_block_enabled, off by default) yes, if enabled never

The full comparison is on Engines: Race and Hoard. The drain, graduation and seed obligations are on Race Drain and Graduation.

Categories decide where a torrent goes

A category is a name with a Mode (race or hoard) and a Save Path. You manage them on the Categories tab. When a torrent is added:

  • a category with mode hoard sends it to the hoard engine; any other mode sends it to race;
  • an unknown category (or no category) sends it to race;
  • the save path is the one given in the request, else the category's save path;
  • there is no default save path: with neither, the add is refused (no save path: …).

A category mode cannot name an extra engine. To place a torrent on vpn1, give an explicit engine on a native REST API add, an MCP add or a watched folder: it wins over the category's mode. The native add routes and the Add tab can also give a mode (race or hoard), which picks the engine of that role. The qBittorrent shim has no way to name an engine. An engine name that does not exist is refused with HTTP 400 by both native add routes (POST /api/torrents/upload and POST /api/torrents, magnets included), refused by an MCP add of a file (a tool result with isError: unknown engine …), and saving a watched folder that names one is refused (400 no engine "…"); an MCP magnet ignores it and falls back to the category (as does a watched folder whose engine was removed after it was saved).

flowchart TD
    A["Torrent added, with or without a category"] --> E{"Explicit engine given?<br/>(REST, MCP, watched folder)"}
    E -- "yes, and it exists" --> EX["That engine"]
    E -- "yes, but unknown" --> UK{"Which add path?"}
    UK -- "native add routes, MCP file, saving a watched folder" --> R400["Refused: no engine with that name"]
    UK -- "MCP magnet, watched folder whose engine was removed" --> K
    E -- no --> MO{"mode given?<br/>(native routes, Add tab)"}
    MO -- yes --> ROLE["The engine with that role"]
    MO -- no --> K{"Category known?"}
    K -- "yes, mode hoard" --> HO["Engine hoard"]
    K -- "yes, any other mode" --> RA["Engine race"]
    K -- no --> RA
    EX --> SP{"Save path in the request?"}
    ROLE --> SP
    HO --> SP
    RA --> SP
    SP -- yes --> U["Use it"]
    SP -- no --> CP{"Category has a save path?"}
    CP -- yes --> UC["Use the category save path"]
    CP -- no --> REF["Refused: no save path"]
Loading

The complete placement rule, including the race add-block refusal and the path claim (a torrent whose files another torrent already holds), is on Categories and Routing.

Downloading vs seeding what you already have

What happens after placement depends on what is already in the save path.

  • Download. No file of the torrent exists at the save path: the engine downloads it.
  • Recheck existing data. At least one file already exists: Hydranos hash-checks what is on disk, seeds the pieces that verify and downloads the rest. This is the default on every add path, so pointing a torrent at existing data is safe; it only costs disk reads.
  • Seed mode. The caller asserts the data is complete (seed_mode, or skip_checking on the qBittorrent shim): the torrent starts seeding at once, with no hash check and no check that the files even exist. Use it only when you are sure. On the + Add Torrent tab it is the box Skip the hash check, the data is already on disk; the import wizard applies it automatically to torrents that are complete in the old client and whose data it finds.

Torrents that download wait for a free slot when the engine runs a queue (active_downloads, Queue, Share and Speed Limits). Details, magnets, watched folders and duplicate-data hardlinking: Adding Torrents and Existing Data.

Where Hydranos keeps its state

What File Holds
Config file default.toml, given by --config (default /config/default.toml; on Windows default.toml beside hydranos.exe) settings, engines, network mode, the API key, the admin account
Store <data_dir>/hydra.db the catalogue: every torrent's .torrent bytes, category, tags, stopped state, force-download pins, per-torrent share limits, lifetime counters, workflows, jobs, magnets, RSS feeds and rules, nodes, settings saved from the UI (IP filter, watched folders, client-wide speed caps)
Resume state <config dir>/<engine id>/state.db, one per engine progress (which pieces are verified), save path, edited trackers, bytes up and down, seeding time, per-torrent speed caps, download order

data_dir is a key of [daemon]. In the Docker image it is /config, so everything sits in the one /config folder. When the binary writes its own first config elsewhere, data_dir is the data folder next to the config file.

The store is the only copy of the metainfo Hydranos reads at startup: it does not fall back to .torrent files. Back up hydra.db and the engine folders together. Files per platform and the startup order: Architecture. Backups and clean shutdown: Operating Hydranos.

Talking to Hydranos

Everything is served by the one process on port 8199 ([daemon] api_port), and one credential opens all of it: the API key ([daemon] api_key, generated at startup if empty, as shipped, or still set to the old published placeholder change-me-in-production). A session cookie from a username/password login (the web UI, the qBittorrent shim's login) is accepted on the same routes.

Interface Path For Page
Web UI / you, in a browser; log in with the admin account Web UI Tour
Native REST API /api/* scripts; header X-Api-Key: YOUR_API_KEY REST API
qBittorrent shim /api/v2/* Sonarr, Radarr, autobrr, cross-seed configured as "qBittorrent" qBittorrent Shim and Automation
MCP server POST /mcp AI clients that speak the Model Context Protocol MCP Server
curl -s -H 'X-Api-Key: YOUR_API_KEY' http://127.0.0.1:8199/api/categories

How the key, the admin account and the shim login fit together: Security and Access.

Nodes: more than one machine

A node is another complete Hydranos instance, on another machine, that this instance knows about. There is no special node mode: the node runs the same hydranos, with its own engines, store and API key. The instance where you added it is its controller. You either enrol a new machine (+ Enrol a machine: a one-time token and an install command) or declare a running instance (+ Declare an existing one: URL and its API key).

The controller calls the node's normal HTTP API with the node's key, and the node never calls the controller, with one exception: at the end of an enrolment, the install script registers the new machine once with the controller (POST /api/nodes/register), using the one-time token instead of an API key. The controller shows the node's health and merges its torrents into the hoard list, and torrents can be pushed to or pulled from a node. Data travels between the engines over BitTorrent, never through the API. A few per-torrent actions (Edit tags ›, Force download, Share limits…) are not relayed to rows that belong to another node: see Nodes and Multiple Machines.

Automation

  • Workflows (tab Workflows): a trigger (on a timer, when a download completes, when a torrent is added), conditions in a When tree, and actions in Then. See Workflows.
  • RSS (tab RSS): feeds fetched on their own interval, and automatic download rules that add matching articles through the normal add pipeline. See RSS and Automatic Downloads.
  • Queue and share limits: per engine, how many torrents run at once and when a seed is stopped or removed (ratio, seeding time). See Queue, Share and Speed Limits.
  • Jobs (tab Jobs): persistent background data moves (move_data, graduate) that survive a restart. The word "job" means only these.
  • Bulk actions: context-menu actions on a selection of torrents. They run as an in-memory bulk task with a progress panel, are not listed on the Jobs tab, and are lost on restart.

Jobs and bulk actions: Jobs, Bulk Actions and Moving Data.

Glossary

One name per concept; every page of this wiki uses the Term column. "UI label" is what you click; "Config / API name" is what you type.

Term Meaning UI label Config / API name
Hydranos the product HYDRANOS header, Hydranos Torrent Daemon footer binary hydranos
instance one running hydranos process with its config and store — —
node an instance seen from another instance; "this node" is the local one tab Nodes, This node, Other nodes /api/nodes*, store table nodes
controller the instance where a node was enrolled or declared — —
enrol add a new machine with a one-time token and the install script + Enrol a machine POST /api/nodes/enrol, /install.sh
declare add an already-running instance by URL and API key + Declare an existing one POST /api/nodes
engine one BitTorrent session inside the process Nodes › This node: column Engine, + Add an engine [race], [hoard], engine blocks, /api/engines/:id/…
engine id the engine's name: race, hoard, or your own (e.g. vpn1) field Id engine_id / name, ?engine=
role race or hoard behaviour of an engine Role ("how it behaves") role
race engine the engine with id race ("a race-role engine" for extras) tab Race (heading Race Torrents; lists every race-role engine) [race]
hoard engine the engine with id hoard tab Hoard (heading Hoard Torrents) [hoard]
extra engine any engine other than race and hoard Nodes › This node › + Add an engine engine block (TOML array of tables)
copy the same torrent present in more than one engine Duplicate to engine ›, Move to engine › (context-menu group Node) key (info_hash, session)
category named mode and save path that place a torrent tab Categories /api/categories, qBit category
category mode race or hoard; picks the engine Mode mode
save path folder where a torrent's data lives Save Path save_path, qBit savepath
graduation moving a torrent and its data to the engine of another mode (race → hoard by the drain or a category change; any direction via Change category + move files ›), with its resume record; rolled back if it fails Graduate to (hoard category), Race tab Graduating graduate_to, job graduate
transit area hoard category flag: when another category graduates into it, the transit sweep deletes its torrents and data once their seed obligation is met Transit area transit
race drain automatic freeing of race volumes Race tab volume cards (Auto drain, Drain now, History), red badge on the Race tab [race_drain], /api/drain/*
volume a filesystem holding race data; the drain works per volume, optionally against a declared quota Race tab volume card /api/drain/policy
quota (volume) a size declared for a race volume (a shared seedbox slot): the drain's percentages are of the quota, not the disk (Race Drain and Graduation) volume card Quota quota_gb
TRIM after drain Linux: tell the SSD which blocks a drain pass freed volume card TRIM after drain trim_after_drain
stuck torrent race torrent the drain can neither delete nor graduate red badge on the Race tab —
seed obligation hours a tracker requires before deletion Trackers › Minimum seed [announce_min_seed_hours]
passkey override per-tracker passkey substitution Trackers › Passkey [announce_passkeys]
announce IP family v4, v6 or auto per tracker host Trackers › Announce (auto / v4 only / v6 only) [announce_ip_modes]
mute / hide (tracker) silence a tracker's errors / hide it from the list Mute / Hide [announce_muted] / [announce_hidden]
announce a request to a tracker Reannounce (context menu) /api/torrents/:info_hash/reannounce
scrape seeder and leecher counts asked of a tracker for torrents not announced yet; starts and stops nothing (Trackers and Announces) column Scrape BEP 48 (HTTP), BEP 15 (UDP)
tier group of tracker URLs tried together; a blank line separates tiers in the editor Edit trackers announce-list
peer id the 20-byte id sent to trackers and peers (-HY, the version, -, then 12 random characters), one per engine, drawn at every start; announces and handshakes carry the engine's own Announced as, Next announce (detail panel) —
recheck hash-check of the data on disk Recheck verify routes, qBit recheck
seed mode add that trusts the data on disk without hashing Skip the hash check, the data is already on disk (Add tab); the import wizard applies it to complete torrents seed_mode, skip_checking
stop / start your stop intent, kept across restarts Stop / Start qBit stop/pause, start/resume
force download pin: marks an incomplete hoard torrent as forced (filter chip Forced): the queue runs it past its ceiling Force download / Stop forcing pin / unpin, pinned
download slots / queue max simultaneous downloads per engine, applied every 30 s by the queue manager (Queue, Share and Speed Limits) Config › Advanced settings (default.toml) › Session Race / Session Hoard active_downloads
queued a torrent the queue stopped until a slot frees state queued (tooltip Waiting for a slot) —
seeding queue qBittorrent's switch that also caps running seeds and the total — queueing, active_seeds, active_limit
share limits stop or remove a seed past a ratio, a seeding time or an inactive time, per engine or per torrent Share limits… (context menu) max_ratio, max_seeding_time, max_inactive_seeding_time, share_limit_action; …/share-limits, qBit setShareLimits
speed caps upload and download limits per torrent, per engine, and client-wide Limit rate… (context menu) upload_rate_limit, download_rate_limit; …/limits, …/rate-limits, qBit setUploadLimit
choker optional per-torrent upload-slot ranking, off by default — choking, max_uploads_per_torrent
startup pause an engine held at start: no dial, no announce until released banner, Start now start_paused, /api/startup-pause
magnet magnet link, resolved to metadata by BEP 9 Magnet link magnet_uri, /api/magnets
watched folder folder polled for .torrent and .magnet files Watched folders /api/watch
RSS feed / RSS rule a feed fetched on its own interval / an automatic download rule that adds matching articles (RSS and Automatic Downloads) tab RSS: RSS feeds, Automatic download rules /api/rss/*, qBit /api/v2/rss/*
duplicate data (dedup) hardlink an identical payload already held on add Duplicate data [dedup] enabled
path claim / path collision the add-time check that keeps a torrent from writing into files another torrent holds with other bytes (Adding Torrents and Existing Data) — [dedup] path_collision (isolate, refuse, legacy)
import one-shot import from qBittorrent or Transmission Import from another client, Import… /api/import/*
facet a filter list of the Hoard tab (Trackers, Categories, Tags, Tracker errors): click a chip to include, right-click to exclude Hoard tab chips —
tag free label on a torrent, no effect on placement Edit tags ›, Hoard Tags facet tags, qBit addTags
workflow trigger, conditions and actions tab Workflows /api/workflows
trigger when a workflow runs Runs: on a timer / when a download completes / when a torrent is added schedule / completed / added
condition a field comparison in the When tree When, + Condition, + Group when tree
action a step in Then Then, + Action actions[]
job persistent background data move, listed on the Jobs tab tab Jobs /api/jobs, move_data, graduate
bulk action / bulk task context-menu action on a selection; runs as an in-memory bulk task, lost on restart context menu /api/selection/:action, /api/selection/jobs/:id (literal route name)
set location move a torrent's data to another folder Set location… …/location
handoff push a local torrent to a node's engine Duplicate to engine › / Move to engine › (node targets) selection handoff
fetch pull a node's torrent into a local engine bring it here selection node-fetch
local row / remote row a list row held by this instance / merged from a declared node row label local-<id> / <node>-<engine> ?agent= (legacy parameter name, kept verbatim)
store <data_dir>/hydra.db, the catalogue including .torrent bytes — hydra.db
resume state per-engine <config dir>/<engine id>/state.db — state.db
config file default.toml Advanced settings (default.toml) --config
config dir the folder containing default.toml — —
data dir where the store lives — [daemon] data_dir
API key the one credential for every interface Config › Advanced settings › General › api_key [daemon] api_key, header X-Api-Key
admin account the single web UI user first run: Welcome to Hydranos, button Create account; later: Hydranos login [auth]
web UI the browser app on port 8199 — /
native API / REST API Hydranos's own HTTP API — /api
qBittorrent shim qBittorrent-compatible HTTP API — /api/v2
MCP server endpoint for AI clients — (no UI) /mcp, [mcp]
Add tab the tab for adding torrents + Add Torrent (heading Add Torrent) —
listen port an engine's TCP and uTP port Listen port listen_port
network mode how the engines reach the network: Direct, managed WireGuard, Gluetun, SOCKS5 proxy, SOCKS5 + PROXY-v2 relay (Networking) Config › Advanced settings › Network, mode cards [network] mode, /api/network/mode
interface pinning binding every socket of an engine (peers, announces, DHT, LSD, webseeds, scrapes) to a network interface, Linux only Interface / Race engine interface bind_interface
managed WireGuard tunnel a tunnel wg-<engine> Hydranos brings up itself from a provider .conf, one per engine (Managed WireGuard) mode WireGuard, managed by Hydranos, Way out wg-<engine>, <data_dir>/wireguard
kill switch keeps an engine with no way out of its own off the network instead of letting it use the default route; armed by default in every mode but Direct (VPN and Egress) card Kill switch and the daemon's own requests [daemon] kill_switch, allow_direct
egress (daemon) the route of the daemon's own requests (lists, update check, webhooks, .torrent URLs) External requests of the daemon [daemon] egress, /api/network/egress
dial queue the queue that opens an engine's outgoing peer connections; one per engine, with its own binding and peer id — —
LSD local peer discovery (BEP 14) on the engine's network — enable_lsd
exit IP public address seen from outside Exit IP /api/public-ip, /api/network/engines
port forwarding automatic NAT-PMP / UPnP mapping, or the port a managed tunnel or gluetun grants — auto_port_forward, /api/port-forward
IP filter block lists and bans IP filter /api/ipfilter
seed size total size of what is seeded, before hardlink sharing Overview › Storage › Seed size —
rescue mode minimal server when the store cannot open — (no web UI; / answers the text hydra: the store could not be opened) /api/store/repair
graceful stop SIGTERM, Ctrl+C, a Windows console event (Ctrl+Break, console closed, sign-out, shutdown), tray quit or a restart from the UI: drain, stopped to the trackers, resume-state flush within HYDRANOS_STOP_TIMEOUT Quit Hydranos (Windows tray) HYDRANOS_STOP_TIMEOUT
⚠️ **GitHub.com Fallback** ⚠️