Concepts - Kheopsian/Hydranos GitHub Wiki
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).
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,
raceandhoard, even if the config file has no[race]or[hoard]section. - You can add extra engines (for example
vpn1) withengineblocks 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,
raceorhoard. The role is a behaviour, not a name: an extra enginevpn1withrole = "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
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.
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
hoardsends it to thehoardengine; any other mode sends it torace; - 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"]
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.
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, orskip_checkingon 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.
| 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.
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/categoriesHow the key, the admin account and the shim login fit together: Security and Access.
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.
- 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.
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 |