REST API FR - Kheopsian/Hydranos GitHub Wiki

🇬🇧 English · 🇫🇷 Français

API REST

S'applique à Hydranos 4.4.

Pour scripter Hydranos : toutes les routes de l'API native (/api/*) en 4.4, celles qui fonctionnent, celles qui sont des bouchons, celles qui ont été retirées, des exemples curl. L'API compatible qBittorrent (/api/v2/*) est documentée sur Shim qBittorrent & automatisation, et l'endpoint MCP sur Serveur MCP.

Cette page est la carte. Les corps complets des requêtes et des réponses sont dans le fichier docs/API.md du dépôt ; les pages liées depuis chaque section expliquent ce que font les réglages.

Bases

URL de base et format

Un seul port, 8199 par défaut ([daemon] api_port), sert l'interface web, /api, le shim qBittorrent et /mcp. Les exemples utilisent :

export HY=http://hydranos.example.org:8199
export KEY=YOUR_API_KEY

Les corps et les réponses sont en JSON, sauf mention contraire dans une ligne. Les réponses de plus de 32 octets sont compressées en gzip quand le client l'accepte (curl --compressed).

Authentification

Il n'existe qu'un seul identifiant, la clé API ([daemon] api_key, 48 caractères hexadécimaux, générée au premier démarrage). Où la trouver, comment la renouveler et comment exposer Hydranos sans risque : Sécurité & accès.

Comment la clé circule Exemple Remarques
En-tête X-Api-Key -H "X-Api-Key: $KEY" La méthode normale.
Paramètre de requête apikey "$HY/api/status?apikey=$KEY" Pour les clients qui ne peuvent pas définir d'en-têtes. Finit dans les journaux du proxy.
Cookie de session SID issu de POST /api/v2/auth/login Formulaire username + password (mot de passe admin ou clé API). Ouvre toute l'API native. Expire après 3600 s d'inactivité ; perdu au redémarrage.
Authorization: Bearer -H "Authorization: Bearer $KEY" Accepté par /mcp uniquement.

POST /api/login (JSON {"username","password"}) ne pose pas de cookie : il renvoie {"api_key": "..."}, que l'interface web envoie ensuite en X-Api-Key. Un api_key vide dans la configuration refuse tous les appelants.

Formes de refus (testez le code de statut, pas le texte) :

Où Statut Corps
/api/* (presque toutes les routes) 401 {"error":"Invalid or missing API key"}
/api/selection/*, /api/network/egress 401 {"error":"unauthorized"}
/api/v2/* (shim qBittorrent) 403 texte brut Forbidden. (Sonarr et Radarr attendent un 403 avant la connexion)
/mcp 401 JSON comme ci-dessus + en-tête WWW-Authenticate: Bearer
POST /api/nodes/register 401 {"error":"enrolment token unknown, already used, or expired"}
POST /api/setup 409 dès qu'un admin existe ; 403 si le pair de la socket n'est ni en loopback ni sur un réseau privé, ou si un pair privé (un reverse proxy) désigne un client public dans X-Forwarded-For JSON

Routes publiques (sans clé) : /health, /metrics, /, /static/*, /changelog.md, /install.sh, GET|POST /api/setup, POST /api/login, GET /api/startup, POST /api/v2/auth/login, POST /api/v2/auth/logout, POST /api/nodes/register (jeton d'enrôlement à la place de la clé), et POST /api/auth/api-key/confirm (la clé en attente d'une rotation à la place de la clé actuelle, voir Nœuds).

Pendant le démarrage d'Hydranos

Le port s'ouvre avant que le catalogue soit chargé. Tant que le chargement n'est pas fini, seuls /, /static/*, /health ("status":"starting", 200), /metrics, GET /api/startup et GET /api/setup répondent. Tout autre chemin, /api/v2/* et /mcp compris, répond 503 avec Retry-After: 5 et {"error":"Hydranos is starting (<phase>): N of M torrents restored","startup":{…}} ; une autre méthode sur l'un des chemins ci-dessus (par exemple POST /api/setup) n'est pas routée et est refusée. Interrogez GET /api/startup jusqu'à "ready":true, ou réessayez après Retry-After. Les phases : Exploiter Hydranos.

Erreurs

Les erreurs sont du JSON {"error": "<message>"}, parfois avec d'autres champs :

  • 400 : champ invalide ou manquant (les corps stricts nomment le champ inconnu).
  • 404 : {"error":"torrent not found"}, ou moteur/nœud/job inexistant.
  • 405 : le chemin existe, mais pas avec cette méthode (par exemple POST /api/race/settings depuis la 4.4).
  • 409 : refusé à cause de l'état. Les déplacements de données ajoutent "reason" (hardlinks, space, shared, destination_taken, …) et un plan ; une sélection par filtre qui a grossi répond "reason":"grew" avec le nouveau count ; ajouter un torrent déjà présent répond 409.
  • 410 : une route 3.x retirée en 4.4 ; le corps nomme la remplaçante (Routes retirées en 4.4).
  • 503 + Retry-After: 5 : démarrage en cours (ci-dessus).

POST /api/torrents répond {"error","targets"} avec un statut qui dit pourquoi :

Statut Quand
400 Corps qui n'est pas du JSON ; pas exactement un seul champ parmi torrent_path, torrent_url, magnet_uri ; engine ou mode inconnu ; .torrent illisible ou non récupéré (l'URL apparaît sous la forme <url> : elle peut contenir une passkey) ; pas de chemin de sauvegarde (aucun envoyé, aucune catégorie qui en a un) ; magnet invalide (y compris un info hash mal formé, absent ou uniquement v2).
404 torrent_path n'existe pas sur l'hôte Hydranos.
409 Déjà ajouté (torrent already added), et les autres refus : race disk full: … et, avec [dedup] path_collision = "refuse", N file(s) of this torrent are already another torrent's with other bytes ….
500 Une panne du store seulement (store: …).

POST /api/torrents/upload répond aux mêmes refus par un 400, et par 409 pour déjà ajouté. Testez la classe du statut, et lisez error pour la raison.

Hashes, moteurs et copies

  • :info_hash est le hash v1 hexadécimal de 40 caractères. Certaines routes résolvent un préfixe plus court et agissent sur la première correspondance : envoyez toujours le hash complet.
  • Les moteurs sont adressés par identifiant sous /api/engines/:id/... ; /api/race/... et /api/hoard/... sont des alias pour les deux moteurs qui existent toujours. Exception : GET /api/race/torrents liste tous les moteurs de rôle race.
  • Le même torrent peut être dans plusieurs moteurs (une copie par moteur). Les routes de torrent choisissent une copie avec ?engine=<id>. L'ancienne graphie ?agent= (un nom de la 3.x) est aussi acceptée : local ou vide signifie la valeur par défaut, local-<id> signifie le moteur <id>.
  • Les lignes de liste portent une étiquette dans le champ agent (nom historique) : local-<engine> pour une copie locale, <node>-<engine> pour une ligne fusionnée depuis un nœud déclaré. Les routes par torrent répondent 404 pour une ligne de nœud ; /api/selection/* relaie l'action au nœud qui la détient.

Exemples rapides

Statut (champs : Supervision) :

curl -s -H "X-Api-Key: $KEY" "$HY/api/status"

Les 100 premiers torrents hoard de la catégorie tv, les plus récents d'abord (filtre et pagination côté serveur) :

curl -s --compressed -H "X-Api-Key: $KEY" \
  "$HY/api/hoard/page?category=tv&limit=100&sort=added_time"

Créer une catégorie (il n'y a pas de chemin de sauvegarde par défaut, les ajouts en ont donc besoin d'un) :

curl -s -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"name":"tv","save_path":"/data/tv","mode":"hoard"}' "$HY/api/categories"

Ajouter un fichier .torrent avec une catégorie (multipart) :

curl -s -H "X-Api-Key: $KEY" \
  -F "[email protected]" -F "category=tv" "$HY/api/torrents/upload"

Ajoutez -F "seed_mode=true" pour seeder des données déjà sur disque sans les hacher (mode seed ; risques : Ajouter des torrents & données existantes).

Arrêter tous les torrents hoard de la catégorie old (une action groupée par filtre ; comptez d'abord, envoyez le compte comme expect) :

N=$(curl -s -H "X-Api-Key: $KEY" "$HY/api/hoard/page?category=old&limit=1" | jq .filtered)
curl -s -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  -d "{\"selection\":{\"filter\":\"category=old\",\"view\":\"hoard\",\"expect\":$N},\"params\":{}}" \
  "$HY/api/selection/stop"
# 202 {"job":"...","total":N}  ->  poll GET /api/selection/jobs/<job>

Torrents et ajout

Statut : ✓ fonctionne · ◐ fonctionne avec une réserve. Les routes bouchons n'apparaissent que dans Bouchons et routes retirées en 4.4.

Route Méthode Rôle Statut
/api/torrents/upload POST multipart Ajoute un .torrent. Partie fichier torrents, torrent ou file ; champs category, savepath/save_path, tags, engine ou mode (race/hoard), paused/stopped, skip_checking/seed_mode/skip_recheck, create_subfolder. Corps limité à 2 Mo. ✓
/api/torrents POST JSON Ajoute depuis exactement un champ parmi torrent_path (un fichier sur l'hôte Hydranos), torrent_url (récupéré par Hydranos, par la sortie du démon) ou magnet_uri ; plus category, save_path, tags, engine ou mode, stopped, seed_mode (ou skip_recheck), create_subfolder (par défaut [daemon] create_torrent_folder). Magnet → 202 {"info_hash","status":"resolving"}, avec seed_mode et stopped appliqués une fois résolu. Codes de statut : Erreurs ci-dessus. ◐ certains refus sont des 500
/api/torrents/:info_hash DELETE Supprime. ?delete_files=true efface les données ; ?engine= supprime une copie, sinon toutes. Les fichiers qu'un autre torrent lit encore sont gardés. Retire aussi un torrent qu'un moteur fait tourner sans ligne dans le store. ✓
/api/torrents/:info_hash/{files,torrent} GET Liste des fichiers (index, path, size, done, progress, priority, plus sequential, first_last_piece, complete) ; les octets du .torrent depuis le store. ✓
/api/torrents/:info_hash/download-order GET, POST Nouveau en 4.4. Priorités des fichiers (1, 6, 7 ; 0 → 409), sequential, first_last_piece (Ordre de téléchargement). ✓
/api/torrents/:info_hash/limits GET, POST Nouveau en 4.4. Plafonds de débit propres {"up_kib","down_kib"} (0 = aucun), en direct et enregistrés. ✓
/api/torrents/:info_hash/share-limits GET, POST Nouveau en 4.4. ratio_limit, seeding_time_limit, inactive_seeding_time_limit propres (-2 = ceux du moteur, -1 = aucun). ✓
/api/torrents/:info_hash/trackers GET, POST Lire / modifier. POST {"op":"add"|"remove","urls":[...]}, {"op":"replace","from","to"} ou {"op":"set","tiers":[ ["u1"], ["u2"] ]}. .../add-tracker prend {"url"}. ✓
/api/torrents/:info_hash/reannounce POST Annonce immédiate, une seule copie : celle de ?engine=, sinon le premier moteur qui la détient. 429 pendant le délai de 60 s. ✓
/api/torrents/:info_hash/peers POST {"peers":["203.0.113.5:16172"]} : se connecter à ces pairs. ✓
/api/torrents/:info_hash/copy POST {"engine":"vpn1"} : ajoute une seconde copie dans un autre moteur, mêmes fichiers. 409 tant que la source n'est pas complète ou est en cours de vérification. ✓
/api/torrents/:info_hash/engine POST {"engine":"vpn1"} : déplace le torrent vers un autre moteur, les fichiers restent en place. ?engine= désigne la copie source. La cible reprend l'état de la source (pièces, trackers, compteurs, arrêté) avant que la source ne le lâche ; si l'un des deux refuse, rien ne change. ✓
/api/torrents/:info_hash/graduate POST {"engine","category","save_path","allow_breaking_hardlinks":false} : met en file un job graduate (déplacement des données + changement de moteur). save_path vaut par défaut celui de la catégorie. Mêmes contrôles qu'un changement de catégorie (409 avec reason). ✓
/api/torrents/add-defaults GET {"create_subfolder","skip_recheck"} : les valeurs de départ de l'onglet d'ajout (create_subfolder = [daemon] create_torrent_folder ; skip_recheck toujours false). ✓
/api/torrents/export POST form format=zip|txt|csv, selection (JSON) ou hashes, strip_trackers=1. Renvoie un téléchargement en flux. ✓
/api/{hoard,race}/torrents/:info_hash GET Une ligne avec le détail en direct. ✓
/api/{hoard,race}/torrents/:info_hash/category POST {"category","move_files":false,"allow_breaking_hardlinks":false}. Avec move_files, 202 + job (ou une graduation si le mode diffère). ✓
/api/{hoard,race}/torrents/:info_hash/location POST Déplacer vers un dossier (Set location) : {"location":"/abs/path","allow_breaking_hardlinks":false}. 202 + job quand des octets bougent, 200 "moved":false s'ils y sont déjà. ✓
/api/{hoard,race}/torrents/:info_hash/move-preview GET Ce que ferait un déplacement, sans le faire. ✓
/api/{hoard,race}/torrents/:info_hash/tags POST {"tags":[...]} remplace tout l'ensemble. ✓
/api/{hoard,race}/torrents/:info_hash/{pause,resume} POST Arrête / démarre un torrent. ✓
/api/hoard/torrents/:info_hash/{pin,unpin} POST Forcer le téléchargement (démarré, passe outre la file de téléchargement) / arrêter de forcer. ✓
/api/hoard/torrents/:info_hash/verify POST Revérification (?engine= choisit la copie), protégée (fichiers partagés). ✓
/api/race/torrents/:info_hash/purge POST Supprime la copie du moteur race avec ses fichiers (les fichiers qu'un autre torrent lit sont gardés) ; les autres copies ne sont pas touchées. 404 torrent not in race. Moteurs race supplémentaires : DELETE /api/torrents/:info_hash?engine=<id>. ✓
/api/race/timeline/:info_hash, /api/trackers GET Chronologie race ; tableau de l'onglet Trackers. ✓

Comment un ajout choisit son moteur et son chemin de sauvegarde : Catégories & routage. Sans engine ni mode, c'est la catégorie qui décide. create_subfolder donne à un torrent à fichier unique un dossier à lui, au nom du torrent ; il n'est pas appliqué aux magnets sur cette route.

Listes et moteurs

Chaque ligne /api/engines/:id/... répond aussi en /api/hoard/... et /api/race/..., sauf pinned, pause-all et resume-all (alias hoard uniquement) et rate-limits, share-limits (sans alias : utilisez /api/engines/race/… ou /api/engines/hoard/…).

Route Méthode Rôle Statut
/api/engines GET Moteurs locaux (id, role, listen_port, torrents…), chacun avec ses stats en direct (états, débits, pairs, annonces, plafonds, listening, held…). ✓
/api/engines POST {"id","role":"race"|"hoard","listen_port","bind_interface"} : ajoute un moteur supplémentaire à la configuration. Répond restart_required: true. Un bind_interface non vide hors Linux → 400. ✓ (après redémarrage)
/api/engines/:id DELETE Retire un moteur supplémentaire de la configuration. 409 avec le compte tant qu'il détient des torrents ; race/hoard → 400. Un moteur vide garde son listener jusqu'au redémarrage. ✓ (après redémarrage)
/api/engines/:id/page GET Liste paginée (voir ci-dessous). ✓
/api/engines/:id/torrents GET Le moteur en un seul tableau. Sans filtre, offset/limit sont ignorés ; avec un filtre, ils ne le sont pas (voir ci-dessous). ✓
/api/engines/:id/pinned GET Hashes en téléchargement forcé. ✓
/api/engines/:id/pause POST {"hashes":[...],"paused":true} : arrête/démarre des hashes exacts. ✓
/api/engines/:id/torrents/bulk POST {"action":"stop"|"start","hashes":[...],"exclude":[...],"all":false}. Champs inconnus → 400 ; un hashes vide ne veut pas dire « tous ». ✓
/api/engines/:id/{pause-all,resume-all} POST Arrête / démarre tous les torrents du moteur. ✓
/api/engines/:id/listen-port POST {"port":16172} : déplace TCP et uTP à chaud, les annonces suivent, écrit dans la configuration. Port impossible à attacher, déjà pris, ou fixé par gluetun/WireGuard → 409 ; moteur hors réseau → 503. ✓
/api/engines/:id/dial-limits POST {"max_dials_per_sec","max_connections"} (0 = illimité) : à chaud et écrit dans la configuration. ✓
/api/engines/:id/rate-limits GET, POST Nouveau en 4.4. {"upload_kib","download_kib"} (0 = illimité), en direct et enregistrés. ✓
/api/engines/:id/share-limits GET, POST Nouveau en 4.4. max_ratio, max_seeding_time, max_inactive_seeding_time (-1 = désactivé, minutes), share_limit_action. ✓
/api/hoard/stats GET Totaux. ✓
/api/hoard/download-slots GET active_downloads configuré ; les autres compteurs valent 0. ◐
/api/race/settings GET Écho des réglages race (upload_rate_limit/download_rate_limit = plafonds configurés, octets/s). GET seulement depuis la 4.4. ◐
/api/startup-pause, /api/startup-pause/release GET, POST {"held":[engines],"holding"} ; POST lève la retenue (released). Avec start_paused, un moteur retenu ne se connecte à personne et n'annonce rien jusqu'à sa libération. ✓

Ce que font la file d'attente, les limites de partage et les limites de débit : File d'attente, limites de partage et de débit.

Paramètres de pagination de .../page : offset (par défaut 0), limit (par défaut 500, borné entre 1 et 5000), sort (un champ de ligne, par défaut added_time), order=asc (décroissant par défaut), facets=1 (ajoute les comptes par catégorie/tag/tracker), fields=hash (hashes seulement). Filtres : search (mots combinés en ET, ou un préfixe de hash hexadécimal de 6 caractères ou plus), category, tag, tracker, error_class et leurs variantes _not (listes séparées par des virgules, __none__ = sans valeur), state (un état de ligne comme seeding, ou __active__, __error__, __tracker_err__, __pinned__). La réponse est {"total","filtered","offset","limit","rows","facets"}. Une page fusionne aussi les autres moteurs locaux du même rôle et les lignes des nœuds déclarés. Depuis la 4.4, les mêmes filtres s'appliquent aussi aux routes de liste complète GET /api/{hoard,race}/torrents et /api/engines/:id/torrents ; sans filtre, elles renvoient le moteur entier (/api/race/torrents : tous les moteurs de rôle race) et ignorent offset/limit. Avec un filtre, n'envoyez pas offset/limit : un limit inférieur à 5000 réduit la réponse à une seule page.

Bug connu en 4.4. Sur une route de liste complète, un filtre plus un offset (avec limit absent ou à 5000 et plus) boucle sans fin dès que 5000 lignes correspondent à partir de cet offset : la route pagine en interne en ajoutant son propre offset, celui de l'appelant vient en premier et l'emporte, donc la fenêtre n'avance jamais.

Sélections (actions groupées)

POST /api/selection/:action exécute une action sur de nombreux torrents sous forme de tâche groupée en mémoire (le menu contextuel de l'interface web l'utilise). Elle répond 202 {"job","total"} ; interrogez GET /api/selection/jobs/:id (conservé 1 h après la fin, perdu au redémarrage). POST /api/selection/jobs/:id/cancel arrête entre deux torrents. Ces tâches ne sont pas les jobs persistants de l'onglet Jobs : voir Jobs, actions groupées & déplacement.

Corps : {"selection": ..., "params": {...}}, strict (clés inconnues → 400).

  • Par lignes : "selection":{"items":[{"hash":"<40 hex>","agent":"local-hoard"}]}. Chaque ligne agit sur sa propre copie (le moteur de son étiquette agent).
  • Par filtre : "selection":{"filter":"category=tv&state=seeding","view":"hoard","expect":<count>}, "exclude":[items] facultatif. filter ne prend que les clés de filtre listées sous Listes et moteurs ; view est un identifiant de moteur (par défaut hoard) ; expect est obligatoire.
Action params
stop, start, pin, unpin, reannounce, recheck {} (recheck couvre aussi les copies race)
tags {"tags":["a"],"op":"add"|"remove"}
category {"category","move_files":false,"allow_breaking_hardlinks":false}
location {"location":"/abs/path","allow_breaking_hardlinks":false}
remove {"delete_files":false}
copy, move-engine {"engine":"vpn1"}
handoff {"node","engine","then":"keep"|"remove"}
node-fetch {"node","from_engine","engine"}
node-move {"node","engine"}
limits (4.4) {"up_kib","down_kib"} : les plafonds de débit propres de chaque torrent.
share_limits (4.4) {"ratio_limit","seeding_time_limit","inactive_seeding_time_limit"}.
download_order (4.4) {"sequential","first_last_piece"} (les priorités de fichiers sont par torrent, sur download-order).

Catégories et tags

Route Méthode Rôle Statut
/api/categories GET, POST Lister / créer {"name","save_path","mode":"race"|"hoard","graduate_to","transit"} → 201. mode insensible à la casse, race par défaut, autre valeur → 400 ; nom déjà pris → 409. ✓
/api/categories/:name PUT, DELETE Mettre à jour : les champs envoyés remplacent ceux enregistrés, les autres (mode compris) sont gardés ; un name différent dans le corps la renomme, réétiquette ses torrents (relabelled ; 409 si le nom est pris) et renomme chaque référence à elle (followed : nombres de rss_rules, watch_folders, workflows, magnets, jobs) / supprimer : l'étiquette est retirée de ses torrents (cleared). ✓
/api/categories/orphans GET Étiquettes portées par des torrents qui ne correspondent à aucune catégorie : [{"name","torrents","mode","save_path"}]. ✓
/api/tags GET Tags. ✓

Trackers et annonces

Route Méthode Rôle Statut
/api/announce/{health,policy}, /api/announce/errors?host= GET Santé des trackers, politique en vigueur, détail des erreurs. ✓
/api/announce/ip-modes GET, POST {"host","mode"} famille IP par tracker. ✓
/api/announce/passkeys GET, POST {"host","passkey"} (une passkey vide efface). ✓
/api/announce/min-seed POST {"host","hours"} ou {"host","clear":true}. ✓
/api/announce/{mute,hidden} POST {"host","muted"} / {"host","hidden"} ou {"hosts":[...],"hidden"}. ✓

Signification de chaque réglage : Trackers & annonces.

Workflows et jobs

Route Méthode Rôle Statut
/api/workflows GET, POST Lister / créer ou remplacer un workflow. ✓
/api/workflows/:id, /api/workflows/:id/run DELETE, POST Supprimer ; exécuter maintenant (?dry=1 : exécution à blanc). ✓
/api/workflows/{fields,activity,links}, .../preview GET, POST Champs de l'éditeur, journal d'activité, état des liens ; POST preview : torrents qu'un brouillon toucherait. ✓
/api/jobs, /api/jobs/:id GET Jobs persistants (?limit=, par défaut 100) ; un job. ✓
/api/jobs/:id DELETE Annuler : cancelled (en file, ou un déplacement vers un nœud en état waiting) ou cancelling (une graduation en cours, annulée et défaite) ; un autre déplacement en cours → 409 (il va jusqu'au bout) ; identifiant inconnu → 404. ✓

Corps : Workflows, Jobs, actions groupées & déplacement.

Drain race

Route Méthode Rôle Statut
/api/drain/{status,history,graduations} GET Volumes, drains passés, graduations en cours. ✓
/api/drain/now?volume=, /api/drain/policy POST Drainer maintenant ; {"volume","enabled","high_watermark","low_watermark"} ou {"volume","inherit":true}, plus en option quota_gb (capacité déclarée, en 10^9 octets, 0 la retire) et trim_after_drain (booléen), chacun seul ou avec l'une ou l'autre forme. ✓

Voir Drain race & graduation.

Nœuds

Route Méthode Rôle Statut
/api/nodes GET, POST Lister (sondés en direct) / déclarer {"name","url","api_key"}. ✓
/api/nodes/test POST Sonder sans enregistrer. ✓
/api/nodes/:name DELETE, PATCH Oublier un nœud (la machine distante n'est pas touchée) / le modifier (4.4) : n'importe lesquels de {"name","url","api_key"} ; une nouvelle URL ou clé est d'abord sondée (400 si elle ne répond pas), un nom déjà pris → 409, les déplacements en attente suivent un renommage. ✓
/api/nodes/:name/rotate-key POST Nouveau en 4.4 (nœud en 4.4). Le nœud reçoit une nouvelle clé, enregistrée ici et renvoyée une seule fois ; l'ancienne est refusée. 502 avec la raison si le nœud a refusé (il garde son ancienne clé). ✓
/api/auth/api-key/rotate, .../confirm POST Côté nœud d'une rotation : une clé en attente (10 min) / l'adopter, en s'authentifiant avec cette clé (écrite dans [daemon] api_key). ✓
/api/nodes/enrol, /api/nodes/register POST Jeton à usage unique + commande d'installation ; appelé par la nouvelle machine avec le jeton. ✓
/api/nodes/:name/{handoff,fetch,move-engine} POST Pousser / rapatrier / déplacer entre les moteurs de ce nœud (corps : Nœuds & plusieurs machines). ✓
/node/:name/open GET Redirige vers l'interface du nœud avec sa clé. ✓
/install.sh GET Script d'enrôlement (public). ✓

Les anciennes routes 3.x /api/agents* ont été retirées en 4.4 : tout chemin et toute méthode sous /api/agents répondent 410 Gone avec "see":["/api/engines","/api/nodes"]. Utilisez /api/engines pour les moteurs de cette machine et /api/nodes pour les autres machines (Mettre à jour depuis la 3.x).

Magnets, dossiers surveillés, import, dédup

Route Méthode Rôle Statut
/api/magnets, /api/magnets/:hash, /api/magnets/:hash/retry GET, DELETE, POST Magnets en résolution ou en échec ; abandonner ; réessayer. ✓
/api/watch GET, PUT Dossiers surveillés. PUT prend la liste complète [{"path","category","engine","paused","enabled"}] ; chemin absolu et existant, la catégorie doit exister. ✓
/api/import/qbit/{preview,start}, .../{status,events} POST, GET Import depuis une instance qBittorrent ; progression (events est en SSE). ✓
/api/import/transmission/{upload,preview,start} POST Import depuis Transmission (upload multipart, jusqu'à 4 GiB). ✓
/api/import/retry POST Nouveau en 4.4. Sans corps ; relance ce que le dernier import a raté, avec les mêmes choix, et répond {"job_id","torrents"} (le nouveau job, les torrents relancés) ; 404 aucun import, 409 un import en cours, 400 rien à relancer ou la connexion à qBittorrent a échoué (la relance reste disponible). ✓
/api/import/check-paths POST {"paths":[...]} : quels chemins cet hôte voit. ✓
/api/provenance GET D'où la bibliothèque a été importée ; écrit par chaque import qBittorrent ou Transmission qui a ajouté au moins un torrent. ✓
/api/dedup/config, /api/dedup/stats POST, GET {"enabled":true} (redémarrer pour appliquer) ; compteurs des données en double. ✓

Assistant d'import : Migrer depuis qBittorrent. Magnets, dossiers surveillés, dédup : Ajouter des torrents & données existantes.

RSS

Nouveau en 4.4. Flux, dossiers et règles de téléchargement ; une règle est le document de règle de qBittorrent (rss/setRule). Les routes /api/v2/rss/* des clients qBittorrent atteignent les mêmes flux et les mêmes règles.

Route Méthode Rôle Statut
/api/rss GET Tout ce que montre l'onglet RSS : réglages, feeds, folders, rules. ✓
/api/rss/feeds, /api/rss/feeds/:id GET, POST / PATCH, DELETE Lister, ajouter {"url","path","refresh_secs"} (409 si déjà pris) / modifier ; retirer. ✓
/api/rss/feeds/:id/{refresh,articles,read,download}, /api/rss/refresh POST, GET Récupérer maintenant ; articles ; marquer comme lu ; ajouter un article à la main ; tout récupérer (202). ✓
/api/rss/folders POST {"path"}. ✓
/api/rss/rules, /api/rss/rules/:name, .../rename GET, PUT, DELETE, POST Lister ; créer ou remplacer (400 pour une regex ou un filtre d'épisodes invalide) ; retirer ; renommer. ✓
/api/rss/rules/:name/matching, /api/rss/preview GET, POST Ce qu'une règle enregistrée, ou un brouillon, prendrait parmi les articles déjà récupérés. N'ajoute rien. ✓

Règles, filtres et sort d'un article : RSS et téléchargements automatiques.

Réseau et filtre IP

Route Méthode Rôle Statut
/api/network/{interfaces,engines}, /api/public-ip GET Interfaces actives ; IP de sortie par moteur (?refresh=1) ; IP de sortie du processus. ✓
/api/network/mode GET, POST Onglet Réseau. POST écrit les clés du mode choisi et retire celles des autres modes ; restart_required. bind_interface hors Linux → 400. ✓
/api/network/egress GET, POST Nouveau en 4.4. Le kill switch (armed, pourquoi, moteurs bloqués) et la sortie des requêtes propres du démon (auto, direct, proxy, engine:<id>). ✓
/api/network/wireguard, .../configs, .../configs/:name, .../engines GET, POST, DELETE, POST WireGuard géré : état et tunnels actifs ; téléverser un .conf de fournisseur ; en retirer un ; affecter les moteurs (tunnel, direct, none). ✓ (Linux)
/api/network/check POST Test de fuite par moteur : fail avec LEAK: quand un moteur censé sortir par une interface ou un proxy montre l'adresse de la route par défaut. ✓
/api/port-forward GET Par moteur : port tenu, inbound_peers, reachable (yes/no/unproven), qui redirige le port. ✓
/api/ipfilter GET, PUT État / {"enabled","sources":[...],"refresh_hours"}. ✓
/api/ipfilter/bans, /api/ipfilter/reload POST, DELETE Bannir {"ip","reason"} / débannir {"ip"} ; POST reload recharge les listes de blocage. ✓

Corps : docs/API.md. Les modes et l'onglet Réseau : Réseau : ports & redirection ; kill switch et sortie du démon : VPN & sortie réseau ; tunnels : WireGuard géré.

Réglages, installation initiale, redémarrage

Route Méthode Rôle Statut
/api/setup GET, POST Premier démarrage : {"needs_setup",...} / créer l'admin {"username","password"} (8 caractères ou plus) → clé API. Public. ✓
/api/login POST {"username","password"} → {"api_key"}. Public. ✓
/api/auth/password POST {"password"} (8 caractères ou plus) : enregistré haché dans [auth] password_hash. ✓
/api/settings GET, POST Toute la configuration en JSON, clé API comprise. Les mots de passe SOCKS5 et la clé d'API gluetun valent ******** (renvoyer le masque garde la valeur enregistrée) ; une URL announce_proxy (avec un éventuel user:pass) est encore renvoyée en clair / {"changes":[{"section","key","value"}]} ne modifie que des clés existantes, sauf les clés de moteur à chaud de [race]/[hoard] (débits, choker, file d'attente, limites de partage ; enable_lsd au prochain démarrage), créées si elles manquent. Répond applied_live, restart_required. ✓
/api/settings/reset POST Remet les valeurs par défaut (garde l'identifiant de connexion, la clé, le dossier de données). ✓
/api/settings/restart, /api/restart POST Répond, puis s'arrête proprement (comme sur SIGTERM : stopped envoyé aux trackers, état de reprise écrit) et quitte avec le code 75 pour que le superviseur redémarre (Exploiter Hydranos). ✓
/api/fs/browse?path= GET Liste les dossiers sur l'hôte Hydranos. ✓
/api/update-check GET Nouvelle version disponible. ✓

Mot de passe oublié : hydranos reset-password sur l'hôte (Sécurité & accès).

Supervision

Route Méthode Rôle Statut
/health GET Public. status healthy (200), degraded (200 : un moteur sans listener, retenu par start_paused ou bloqué par le kill switch), unhealthy (503 : le store ne répond pas, ou aucun moteur en ligne n'écoute), starting (200) ; plus checks et problems. ✓
/metrics GET Public, Prometheus, par moteur (séries : Métriques Prometheus). ✓
/api/startup GET Public. {"ready","phase","total","restored","engines","uptime"} (Pendant le démarrage d'Hydranos). ✓
/api/status GET Chiffres de l'en-tête et de la Vue d'ensemble. ✓
/api/events GET Flux SSE d'instantanés de statut. Se termine de lui-même quand le démon s'arrête. ✓
/api/logs, /api/logs/stream GET Le tampon circulaire en mémoire (2000 dernières lignes), filtré côté serveur : level (minimum), module, q, since (5m, 1h, 2d ou Unix s), after (un seq), limit ; un filtre illisible → 400. Suivi en SSE avec les mêmes filtres. ✓
/api/stats/baseline GET, POST Base des compteurs à vie {"total_uploaded","total_downloaded"}. ✓
/api/health/anomalies GET Rapport d'anomalies : contrôles des torrents par une passe de fond toutes les 5 min (generated_at), comptes en direct des trackers, annonces, moteurs et disques. ✓
/api/benchmark/{records,range,current,race-events}, .../trackers/{current,range} GET Données de benchmark, statistiques des trackers. ✓
/api/benchmark/{engines,compare}, .../race-snapshots/:info_hash GET Échantillons des moteurs supplémentaires ; comparaison A/B ; instantanés de la chronologie race. ✓
/api/vpn-speedtest/run, .../{latest,history} POST, GET Test iperf3 (400 non configuré, 409 un test en cours, 502 erreur iperf3) ; résultats enregistrés. ✓

Champs de /api/status, /api/benchmark/current, .../trackers/current et /api/health/anomalies : docs/API.md › Monitoring. /api/status ne couvre que race et hoard (moteurs supplémentaires : stats de /api/engines) ; dans /api/benchmark/current, *_announce_rate, *_announce_fail_rate, race_avg_share et hoard_session_uploaded valent toujours 0.

Ce que rapporte chaque route de supervision : Supervision & journaux.

Bouchons et routes retirées en 4.4

Ces routes répondent mais ne font pas ce que leur nom indique :

Route Ce qu'elle répond À utiliser à la place
GET /api/hoard/download-slots active_downloads configuré et des zéros. active_downloads dans la configuration, lu à chaud (File d'attente, limites de partage et de débit).
GET /api/arr-cleanup/scan Un scan vide ("candidates":null,"count":0). —

Routes retirées en 4.4. C'étaient des bouchons jusqu'à la 4.3 (une réponse en forme de succès qui ne faisait rien) et aucun écran ne les appelait. Un chemin retiré répond 404 ; une méthode retirée sur un chemin qui garde son GET répond 405 ; les routes 3.x répondent 410 avec un corps qui nomme la remplaçante.

Route Depuis la 4.4 À utiliser à la place
POST|DELETE /api/hoard/download-slots 405 active_downloads dans la configuration, lu à chaud.
POST /api/race/settings 405 POST /api/settings ou POST /api/engines/race/rate-limits.
GET|POST /api/opt/flags, GET /api/race/choking 404 —
POST /api/hoard/verify-downloading 404 POST /api/selection/recheck.
POST /api/hoard/restart-stuck 404 stop puis start via /api/selection/*.
POST /api/arr-cleanup/execute 404 —
/api/agents, /api/agents/* (toutes méthodes) 410, "see":["/api/engines","/api/nodes"] /api/engines, /api/nodes.
/api/jobs/move-remote (toutes méthodes) 410, "see":["/api/selection/handoff",…] POST /api/selection/handoff.

Les autres routes listées comme bouchons ou constantes sur la page 4.3 fonctionnent en 4.4 (lignes ci-dessus).

⚠️ **GitHub.com Fallback** ⚠️