Nodes and Multiple Machines FR - Kheopsian/Hydranos GitHub Wiki
S'applique à Hydranos 4.4.
Cette page vous concerne si vous faites tourner Hydranos sur plusieurs machines et voulez voir et déplacer les torrents entre elles depuis une seule interface web. Elle explique comment ajouter une machine (enrôler ou déclarer), ce que le script d'enrôlement lui fait, et quelles actions fonctionnent sur les torrents d'une autre machine. Pour savoir si vous avez vraiment besoin de plusieurs machines : Topologies de déploiement.
Un nœud est une autre instance Hydranos complète : le même programme hydranos, avec son propre fichier de configuration, son store, ses moteurs, sa clé API et son interface web. Il n'existe pas de mode nœud particulier ni de démon de nœud séparé.
- L'instance sur laquelle vous ajoutez un nœud est son contrôleur. Le contrôleur conserve le nom, l'URL et la clé API du nœud dans son propre store (table
nodesdanshydra.db), pas dansdefault.toml. La clé est stockée en clair. - La relation est à sens unique. Le contrôleur appelle l'API HTTP normale du nœud avec la clé API du nœud (en-tête
X-API-Key). Le nœud n'appelle jamais le contrôleur après l'enrôlement, et il ne stocke ni l'adresse ni la clé du contrôleur. - Un nœud continue de tourner seul. Si le contrôleur est arrêté ou le supprime, le nœud continue de seeder et sa propre interface continue de fonctionner. Retirer un nœud sur le contrôleur supprime seulement la ligne (Remove node {name}? Its own data is untouched.).
- Les données des torrents ne passent jamais par l'API. Quand un torrent change de machine, seul le fichier
.torrentpasse par HTTP. Les octets passent par BitTorrent entre les ports d'écoute des deux moteurs, et chaque pièce est vérifiée par hash. - Chaque appel du contrôleur vers un nœud (sonde de santé, liste, action relayée, handoff, rapatriement, rotation de clé) a un délai d'expiration HTTP de 4 secondes.
- Ces appels n'utilisent jamais la route
egressdu démon, son réglage[proxy]ni un tunnel, même kill switch armé (unHTTP_PROXY/HTTPS_PROXYdéfini dans l'environnement du démon n'est pas désactivé, le comportement par défaut du client HTTP peut donc encore l'appliquer) : ils sortent par la route normale de l'hôte (VPN & sortie réseau).
Il existe deux façons d'ajouter un nœud :
| + Enrol a machine | + Declare an existing one | |
|---|---|---|
| À utiliser pour | une machine Linux neuve sans rien d'installé | une instance qui tourne déjà (tout OS, Windows compris) |
| Ce que vous tapez | une commande curl … | sh, sur la nouvelle machine |
nom, URL et clé API du nœud, sur le contrôleur |
| Qui installe Hydranos | le script (Docker, un binaire avec une unité système, ou un binaire dans le home d'un utilisateur) | vous, au préalable (Docker, Linux, Windows) |
| Le contrôleur teste-t-il d'abord le nœud | non | oui, il refuse un nœud qui ne répond pas |
Sur le contrôleur, ouvrez l'onglet Nodes et cliquez sur + Enrol a machine. Le contrôleur crée un jeton à usage unique et affiche une commande comme celle-ci :
curl -fsSL http://192.0.2.10:8199/install.sh | sh -s -- --register-to http://192.0.2.10:8199 --token 0123456789abcdef0123456789abcdef- Le jeton fait 32 caractères hexadécimaux, est valable 30 minutes et utilisable une seule fois. La durée est fixée dans le code et ne peut pas être modifiée.
- L'adresse dans la commande est celle que votre navigateur a utilisée pour joindre le contrôleur (en-tête HTTP
Host). Si c'estlocalhostou un nom que la nouvelle machine ne sait pas résoudre, ouvrez l'interface par une adresse qu'elle peut joindre et cliquez à nouveau. - La commande utilise toujours
http://, même derrière un reverse proxy HTTPS : le jeton et la nouvelle clé API du nœud circulent en clair. - Copier (Copy) copie la commande ; Fermer (Close) masque la carte (le jeton reste valable).
Le script est le fichier install.sh à la racine du dépôt, intégré au binaire et servi sans authentification sur /install.sh. Exécutez-le sur une machine Linux. Il installe Hydranos de l'une de trois façons, choisie dans cet ordre :
| Mode | Quand | Ce qu'il installe |
|---|---|---|
| Docker |
docker est dans le PATH
|
un conteneur |
| système | pas de Docker, lancé en root |
/usr/local/bin/hydranos et une unité systemd |
| utilisateur | pas de Docker et pas root (un compte de seedbox), ou --user
|
~/.local/bin/hydranos et une unité systemd --user
|
Le mode Docker écrit par défaut dans /opt/hydranos : lancez-le en root, ou passez un --dir où vous pouvez écrire. Voici ce que fait le script :
-
Il lit ses options :
--register-toet--token(toutes deux obligatoires),--name(par défaut : la sortie dehostname -s),--port(par défaut8199),--dir(voir l'étape 4),--container(Docker seulement, par défauthydranos),--image(Docker seulement, voir l'étape 5),--user(force le mode utilisateur). Toute autre option l'arrête avec le code de sortie 2. -
Il trouve l'adresse que le contrôleur utilisera pour joindre cette machine : l'adresse source de
ip route get <controller>. En cas d'échec, il s'arrête. -
Il choisit la version à installer : celle du contrôleur, lue sur le
/healthdu contrôleur (tagv<version>), pour qu'un nœud tourne sur la même version que son contrôleur même s'il est enrôlé des mois plus tard. La variable d'environnementHYDRANOS_VERSION_PINla remplace par un tag commev4.4.0(curl … | HYDRANOS_VERSION_PIN=v4.4.0 sh -s -- …). Si aucune des deux ne donne de version, Docker utilise l'image:latestet les modes binaires demandent à GitHub la dernière version. -
Il crée les dossiers de configuration et de données :
Mode Fichier de configuration Dossier de données data_dirécrit dans le fichierDocker <dir>/default.toml(/opt/hydranos)<dir>/data/configs, le dossier tel que le conteneur le voitsystème <dir>/default.toml(/opt/hydranos)<dir>/data<dir>/datautilisateur $XDG_CONFIG_HOME/hydranos/default.toml(par défaut$HOME/.config/hydranos)$XDG_DATA_HOME/hydranos(par défaut$HOME/.local/share/hydranos)le dossier de données En mode utilisateur,
--dirremplace les deux :<dir>/default.tomlet<dir>/data. Si le fichier de configuration n'existe pas, il en écrit un avec une nouvelle clé API aléatoire (48 caractères hexadécimaux) :[daemon] api_host = "0.0.0.0" api_port = 8199 api_key = "<48 hex characters>" data_dir = "<see the table>" [race] listen_port = 16371 enable_ipv6 = true [hoard] listen_port = 16372 enable_ipv6 = true
Un fichier existant est conservé et son
api_keyréutilisée. En mode utilisateur, un nouveau fichier n'est lisible que par son propriétaire (mode 600) : sur une seedbox partagée, la clé est toute l'autorité du nœud. Les ports BitTorrent sont 16371 (race) et 16372 (hoard), pas les habituels 16171/16172 : ouvrez-les (TCP et UDP). -
Mode Docker. Si un conteneur portant le nom
--containerexiste déjà, le script ne le remplace que s'il porte le labelio.hydranos.enrolled=true, c'est-à-dire si ce script l'a créé. Tout autre conteneur arrête le script aveca container named hydranos already exists and was not made by this script; pass --container <another-name>, or remove it yourself. Ensuite il exécute :docker run -d --name hydranos --label io.hydranos.enrolled=true --restart unless-stopped --network host \ --stop-timeout 150 \ -v /opt/hydranos:/configs -v /opt/hydranos/data:/data \ ghcr.io/kheopsian/hydranos:v<controller version> --config /configs/default.toml
--imageremplace le nom d'image entier. Le conteneur utilise le réseau de l'hôte, donc les ports 8199, 16371 et 16372 sont ceux de l'hôte. Le dossier de configuration est monté sur/configs, et le store se trouve dans/opt/hydranossur l'hôte parce quedata_dir = "/configs". -
Mode système (root, pas de Docker). Il télécharge
hydranos-<tag>-linux-amd64.tar.gz(ou-arm64; toute autre architecture l'arrête) depuis la release GitHub, installe/usr/local/bin/hydranos, écrit/etc/systemd/system/hydranos.service(réécrit à chaque exécution ; l'unité tourne en root, avecRestart=always,TimeoutStopSec=150etLimitNOFILE=1000000), puis lancesystemctl enableetsystemctl restart. Sanssystemctl, il affiche la commande pour démarrer Hydranos et se termine en erreur, avant l'enregistrement. -
Mode utilisateur. Il installe le binaire de la même archive dans
$HOME/.local/bin/hydranos, puis :- si
systemctl --userfonctionne dans cette session, il écrit$XDG_CONFIG_HOME/systemd/user/hydranos.service(par défaut$HOME/.config/systemd/user;Restart=always,TimeoutStopSec=150; pas deLimitNOFILE, l'unité relève à la place la limite de fichiers ouverts au maximum du compte), l'active et la redémarre. Il tente ensuiteloginctl enable-lingerpour le compte. Si le lingering reste désactivé, il avertitlingering is off for <user>: the node stops when you log out and stays down after a reboot.et vous demande de faire lancerloginctl enable-linger <user>par l'administrateur de l'hôte ; - sinon (pas de gestionnaire utilisateur, par exemple une session
sshsanspam_systemd), il démarre Hydranos avecnohup, écrit son PID dans<data>/hydranos.pidet affiche la commande pour le relancer : rien ne le redémarre après un plantage ou un redémarrage de la machine. Une nouvelle exécution arrête le processus précédent (même binaire seulement) et l'attend jusqu'à 150 s.
- si
-
Il interroge
http://127.0.0.1:<port>/healthjusqu'à 60 fois, à une seconde d'intervalle, chaque essai disposant de 2 s : entre 1 et 3 minutes environ. Un essai ne compte que si/healthrépond et n'indique plus"status": "starting"(le port s'ouvre alors que le catalogue se charge encore). Si le nœud n'y arrive jamais, il s'arrête sans s'enregistrer et indique où se trouvent les journaux. -
Il envoie
{token, name, url, api_key}à/api/nodes/registersur le contrôleur, avecurl=http://<address from step 2>:<port>. -
Si l'enregistrement échoue, il affiche l'URL et la clé API pour que vous puissiez ajouter le nœud à la main avec + Declare an existing one, et se termine en erreur. Le nœud reste installé et en marche.
Un nom déjà déclaré sur le contrôleur est refusé (HTTP 409) avant que le jeton soit consommé, avec a node named <name> already exists; the token is still valid, try --name : relancez la même commande avec --name <another name>.
Autres limites du script :
- Linux uniquement (
sh,ip,getent,curl,/dev/urandom, ettarhors Docker). Il n'existe pas de script d'enrôlement pour Windows ni macOS ; déclarez plutôt ces instances. - Une route uniquement IPv6 vers le contrôleur produirait une URL où l'adresse IPv6 est écrite sans crochets, que le contrôleur ne peut pas utiliser. Utilisez l'IPv4, ou déclarez le nœud à la main.
- Relancer la commande sur une machine déjà enrôlée conserve sa configuration et sa clé. Elle réinstalle à la version du contrôleur (Docker : recrée son propre conteneur ; système : remplace le binaire et réécrit l'unité), puis l'enregistrement se termine par un 409 (nom déjà pris), sans consommer le jeton : la ligne du nœud existe déjà.
Un nom peut contenir - (seedbox-de, l'exemple affiché dans le formulaire de déclaration, ou une valeur par défaut de hostname -s). Une ligne distante est étiquetée <node>-<engine>, et l'interface comme le serveur comparent cette étiquette aux noms déclarés, du plus long au plus court : avec un nœud nas-2, la ligne nas-2-hoard est le nœud nas-2, moteur hoard. Un nom ne peut pas contenir / ni .. : c'est un segment de chemin du lien Open.
sequenceDiagram
actor Admin
participant C as Contrôleur
participant N as Nouvelle machine
Admin->>C: onglet Nodes, + Enrol a machine
C->>C: POST /api/nodes/enrol, jeton valable 30 min, usage unique
C-->>Admin: commande curl construite depuis l'en-tête Host du navigateur
Admin->>N: colle la commande dans un shell
N->>C: GET /install.sh (sans authentification)
C-->>N: le script
N->>N: adresse propre = source de ip route get contrôleur
N->>C: GET /health (sans authentification)
C-->>N: la version du contrôleur, la version à installer
N->>N: écrit default.toml avec une nouvelle clé API (si absent)
N->>N: conteneur Docker (ne remplace qu'un conteneur étiqueté par le script),<br/>ou binaire + unité système (root), ou binaire + unité utilisateur (--user)
N->>N: interroge GET /health sur 127.0.0.1 jusqu'à ce qu'il ne soit plus starting, 60 essais
N->>C: POST /api/nodes/register avec token, name, url, api_key
alt token, name ou url manquant, URL loopback, ou / ou .. dans le nom
C-->>N: 400, jeton non consommé
else nom déjà connu (vérifié avant le jeton)
C-->>N: 409, jeton non consommé, essayez --name
else jeton inconnu, déjà utilisé ou expiré
C-->>N: 401
else nom pris entre-temps (revérifié après le jeton)
C->>C: jeton consommé
C-->>N: 409, nœud existant non écrasé
else accepté
C->>C: jeton consommé, nœud enregistré, nœud non sondé<br/>une api_key vide est acceptée
C-->>N: 200
end
opt enregistrement échoué
N-->>Admin: affiche URL et clé API pour + Declare an existing one
end
Admin->>C: ouvre l'onglet Nodes plus tard
C->>N: GET /api/status et GET /api/engines (sonde en direct)
Utilisez + Declare an existing one pour une instance déjà installée : Docker, Windows, une machine où le script a échoué, ou une machine 3.x mise à jour.
- Sur le nœud, trouvez sa clé API (Config › Paramètres avancés (default.toml) (Advanced settings (default.toml)) › Général (General) ›
api_key, ou[daemon] api_keydans sondefault.toml). Voir Sécurité & accès. - Sur le contrôleur, onglet Nodes, + Declare an existing one, puis remplissez :
-
Nom (Name) : sans
/ni..(voir Noms des nœuds plus haut). -
URL : l'origine du nœud, telle que les autres machines la joignent, par exemple
http://198.51.100.5:8199. Un/final est retiré. - API key (the remote's own) : la clé du nœud, pas celle du contrôleur.
-
Nom (Name) : sans
- Cliquez sur Test. Il sonde le nœud sans rien enregistrer. En cas de succès, il affiche
Hydranos <version>, <engines>, <n> torrents. En cas d'échec, il affiche la raison (voir le tableau des statuts ci-dessous). - Cliquez sur Ajouter (Add). Le contrôleur sonde à nouveau et refuse (HTTP 400) si :
- le nom ou l'URL est vide, ou le nom contient
/ou..; - l'hôte de l'URL est une adresse loopback (
127.x.x.x,localhost,::1,0.0.0.0) : Open y envoie le navigateur, donc les autres machines doivent pouvoir l'utiliser ; - la sonde échoue.
- le nom ou l'URL est vide, ou le nom contient
Ajouter (Add) avec un nom qui existe déjà remplace quand même son URL et sa clé, sans demander. Pour modifier un nœud, utilisez plutôt Modifier (Edit) sur sa ligne (voir plus bas).
Une URL https:// est acceptée (pour un nœud derrière un reverse proxy). Ce cas n'a pas été testé pour la 4.4, et un handoff vers un tel nœud risque de ne pas l'atteindre (voir Handoff ci-dessous).
L'onglet se charge une fois à son ouverture. Il ne se rafraîchit pas tout seul.
- This node : les moteurs locaux (colonnes Engine, Rôle (Role), Listen port, Interface, IPv6, Socket, Torrents, Actions) et + Add an engine. Les moteurs sont décrits dans Moteurs : race & hoard.
-
Other nodes : une ligne par nœud déclaré, sondé en direct à chaque chargement de l'onglet (tous les nœuds en même temps). Colonnes : Nom (Name), URL, Version, Engines (d'après
GET /api/enginesdu nœud), Torrents (somme sur tous ses moteurs), Statut (Status), Actions. Sans aucun nœud : No other node.-
Open (seulement quand le nœud est en ligne) ouvre l'interface du nœud dans un nouvel onglet, déjà connectée : le contrôleur redirige le navigateur vers
<node URL>/#key=<node API key>, et la page du nœud déplace la clé dans le stockage de son propre navigateur. - Modifier (Edit) change le nom, l'URL ou la clé du nœud (voir Modifier un nœud).
- Changer la clé (Rotate key) (grisé tant que le nœud est hors ligne) donne une nouvelle clé API au nœud (voir Changer la clé d'un nœud).
- Remove supprime la ligne sur le contrôleur. Rien ne se passe sur le nœud. Un déplacement vers ce nœud encore en attente dans l'onglet Jobs échoue alors, et la copie locale est conservée.
- Version est seulement affichée. Rien ne compare la version du contrôleur à celle du nœud, et aucune route ne refuse un écart. Les versions mélangées ne sont pas testées : gardez toutes les machines sur la même release.
-
Open (seulement quand le nœud est en ligne) ouvre l'interface du nœud dans un nouvel onglet, déjà connectée : le contrôleur redirige le navigateur vers
Valeurs de statut (les messages d'erreur viennent du serveur et s'affichent tels quels, en français sans accents) :
| Statut | Signification | Ce qu'il faut vérifier |
|---|---|---|
online |
/api/status a répondu en JSON |
— |
timeout |
pas de réponse en 4 s | nœud arrêté, pare-feu, mauvais port |
connexion refusee |
la connexion n'a pas pu être établie : refusée, échec DNS, pas de route, négociation TLS | port, nom d'hôte, pare-feu, https ou http
|
injoignable |
la requête a échoué après l'établissement de la connexion | reverse proxy, nœud en redémarrage |
cle API refusee |
le nœud a répondu 401 ou 403 | la clé du nœud a changé : déclarez-le à nouveau avec la nouvelle clé |
HTTP <code> |
tout autre statut d'échec | un reverse proxy entre le contrôleur et le nœud |
| une erreur indiquant que la réponse ne vient pas d'Hydranos | 200 mais pas du JSON | autre chose écoute sur cette URL |
Il n'existe pas d'interrupteur « activé » : l'indicateur enabled du store n'est jamais mis à false.
Modifier (Edit) ouvre le formulaire de déclaration rempli avec le nom et l'URL du nœud. Le bouton s'intitule Enregistrer (Save), et le champ API key est vide avec l'indication inchangée (unchanged) : la clé stockée n'est jamais envoyée au navigateur, et un champ vide la conserve. Enregistrer envoie PATCH /api/nodes/<current name> :
- Une nouvelle URL ou une nouvelle clé est d'abord sondée, comme pour Ajouter : refusée (400) si la sonde échoue ou si l'hôte est une adresse loopback. Le formulaire envoie toujours l'URL, donc la sonde a toujours lieu : un nœud hors ligne ne peut pas être renommé depuis l'interface (l'API accepte
{"name": …}seul, sans sonde). - Un nouveau nom suit les mêmes règles que pour Ajouter (pas de
/ni..). Un nom déjà pris est refusé avec un 409a node named <name> already exists. Si l'URL ou la clé a changé dans le même enregistrement, elles sont déjà stockées quand ce 409 revient. - Les déplacements vers ce nœud encore en attente dans l'onglet Jobs suivent le nouveau nom.
- Test dans ce formulaire sonde avec le champ de clé tel que saisi : laissé vide, il teste sans clé.
Changer la clé (Rotate key) demande Changer la clé API de {name} ? (Rotate the API key of {name}?). Ensuite :
- Le contrôleur demande une nouvelle clé au nœud (
POST /api/auth/api-key/rotatesur le nœud, avec la clé actuelle). Le nœud la génère et la garde en attente, en mémoire, pendant 10 minutes ; la clé actuelle fonctionne toujours. - Le contrôleur confirme en présentant la nouvelle clé (
POST /api/auth/api-key/confirm). Le nœud l'écrit dans[daemon] api_keyde sondefault.tomlet refuse l'ancienne clé dès la requête suivante. Les sessions de navigateur connectées par mot de passe sur le nœud restent valables. - Le contrôleur stocke la nouvelle clé et l'affiche une fois (Nouvelle clé API de {name} (New API key of {name}) — Affichée une seule fois. Copiez-la pour les clients de ce node qui utilisaient l'ancienne clé.). Donnez-la à tout ce qui utilisait l'ancienne clé : Sonarr, Radarr ou autobrr pointés sur le nœud, scripts. Open remet la nouvelle clé au navigateur.
Erreurs : the node is unreachable, the node refused the current key: declare it again with its key (corrigez avec Modifier), the node is too old to rotate its key (needs 4.4). Si le contrôleur s'arrête entre les deux étapes, le nœud garde son ancienne clé et oublie celle en attente après 10 minutes ou un redémarrage.
Ce que le contrôleur fusionne :
- La liste Hoard (
/api/hoard/page) affiche les lignes de chaque nœud à côté des lignes locales. La colonne Emplacement (Location) affichelocal-<engine>pour une ligne locale et<node>-<engine>pour une ligne distante (le champ de l'API s'appelle toujoursagent, un nom hérité). La fusion propre au nœud est incluse, donc les moteurs supplémentaires de rôle hoard d'un nœud apparaissent aussi. Un nœud en erreur ou qui met plus de 4 s est écarté de la page sans avertissement. Les compteurs des puces de filtre sont additionnés sur tous les nœuds. - Ctrl+A sur la liste Hoard (« tout ce que le filtre retient ») inclut les lignes distantes.
- L'onglet Race n'affiche pas les lignes distantes : il lit uniquement le moteur race local.
Le tableau ci-dessous donne le résultat de chaque action du menu contextuel depuis l'interface du contrôleur. « Relayée » signifie que le contrôleur envoie l'action au nœud qui détient la ligne, sur la route du nœud lui-même pour la copie de ce moteur ; un refus revient sous la forme <node>: <the node's error>. « Ignorée » signifie que la tâche groupée indique {n} absents de ce nœud ({n} not on this node) (ou {n} non concernés ({n} not applicable) pour une ligne race) et ne fait rien. Les tâches groupées et leur panneau de progression sont décrits sur Jobs, actions groupées & déplacement.
| Action | Ligne locale | Ligne distante (<node>-<engine>) |
|---|---|---|
| Arrêter (Stop) / Démarrer (Start) | fonctionne | relayée (POST /api/engines/<engine>/pause sur le nœud) |
| Re-annoncer (Reannounce) | fonctionne | relayée |
| Vérifier à nouveau (Recheck) (affichée quand la sélection contient une ligne hoard) | fonctionne, lignes race comprises | relayée |
| Retirer le torrent (Remove torrent) / Retirer + supprimer les fichiers (Remove + delete files) | fonctionne, sur la copie sélectionnée seulement | relayée : retire la copie du nœud dans ce moteur |
| Changer de catégorie › (Change category ›) (étiquette seulement) | fonctionne | relayée |
| Changer de catégorie + déplacer les fichiers › (Change category + move files ›) | fonctionne (job ou graduation) | relayée : le nœud déplace ses propres fichiers (un job sur le nœud). Un refus pour cause de liens physiques revient comme une erreur, sans la demande de consentement |
| Déplacer vers un dossier… (Set location…) | fonctionne | relayée, comme ci-dessus |
| Limiter le débit… (Limit rate…), Téléchargement séquentiel (Sequential download), Première/dernière pièce d'abord (First/last piece first) | fonctionne | relayée |
| Modifier les tags › (Edit tags ›), Forcer le téléchargement (Force download) / Ne plus forcer (Stop forcing) | fonctionne sur les lignes hoard | ignorée |
| Limites de partage… (Share limits…) | fonctionne | ignorée |
Duplicate to engine › local-<id> (same files, second identity) |
fonctionne | non proposée |
Move to engine › local-<id> (here, no copy) |
fonctionne | non proposée |
Duplicate to engine › / Move to engine › <node>-<engine> (handoff) |
fonctionne | non proposée |
local-<id> (bring it here) (rapatriement) |
non proposée | fonctionne ; proposée sous Duplicate comme sous Move, et la copie distante est toujours conservée |
Move to engine › <node>-<engine> (there, no copy) |
non proposée | fonctionne, entre deux moteurs du nœud qui détient la ligne |
| Panneau de détail, Edit trackers | fonctionne | le détail est cherché localement et ne se charge pas ; Edit trackers modifie la copie de cette instance pour le même hash, s'il y en a une |
| Exporter (Export) (zip, liste de hashs, CSV) | fonctionne | les fichiers .torrent viennent uniquement du store local ; les hashs présents seulement à distance vont dans missing.txt dans le zip |
Pour les éléments ignorés, cliquez sur Open dans l'onglet Nodes et faites-le dans l'interface du nœud. Une ligne distante dont le nœud n'est plus déclaré échoue avec no declared node holds <label>.
Bug connu en 4.4 : Edit trackers dans le panneau de détail d'une ligne d'un autre nœud semble modifier le torrent de ce nœud — le détail d'une ligne distante ne se charge pas, et le bouton modifie la copie de cette instance pour le même hash, s'il y en a une — cliquez sur Open et modifiez les trackers dans l'interface du nœud.
Le groupe Node (Duplicate to engine ›, Move to engine ›) apparaît dès qu'un nœud est déclaré ou que cette instance a deux moteurs locaux (toute installation par défaut a race et hoard). Le sélecteur de moteur liste d'abord les moteurs de cette instance, puis une section par nœud ; un nœud hors ligne affiche offline, un nœud sans moteur no engine, et si rien ne s'applique vous voyez Nowhere else to send it. Une sélection qui couvre plusieurs nœuds, ou qui mélange lignes locales et distantes, n'obtient aucune cible.
Sélectionnez des lignes locales, puis Duplicate to engine › ou Move to engine › et choisissez <node>-<engine>.
- Si le torrent a une catégorie, le contrôleur demande au nœud ses catégories (un torrent sans catégorie, ou un nœud dont la liste de catégories ne peut pas être lue, saute cette vérification). Si le nœud en a, mais pas celle-ci, le handoff échoue et les liste : créez la catégorie sur le nœud (avec son propre chemin de sauvegarde) et réessayez.
- Il envoie le
.torrentau nœud (POST /api/torrents/upload, avec la catégorie et le moteur choisi). Le nœud le place comme pour tout ajout (Catégories & routage). - Il indique au nœud où se trouvent les données (
POST /api/torrents/<hash>/peersavecauto:<port>). Le nœud remplaceautopar l'IP d'où vient la requête et se connecte au moteur du contrôleur sur ce port. Si le nœud est derrière un reverse proxy, cette IP est celle du proxy et la connexion ne mène nulle part, alors que la tâche indique quand même un succès. - Le nœud télécharge les données depuis le contrôleur par BitTorrent.
Duplicate s'arrête là : les deux instances gardent une copie, et la tâche groupée indique un succès. Move (then: remove) crée en plus un job handoff sur le contrôleur, et la tâche groupée indique {n} envoyé(s) ; la copie locale part quand le node l'a en entier (Jobs) ({n} sent; the local copy goes once the node holds it (Jobs)). Dans l'onglet Jobs (Jobs, actions groupées & déplacement), le job affiche l'état En attente du node (Waiting for the node), la destination <node>-<engine> et la progression du nœud :
- Toutes les 30 s, le contrôleur demande au nœud la progression du torrent (
GET /api/engines/<engine>/page?search=<hash>sur le nœud ; tous les moteurs du nœud quand aucun n'est nommé). N'importe quel moteur convient, y compris les supplémentaires commevpn1. - Quand le nœud indique 100 %, le job est Terminé (Done) et le contrôleur retire le torrent de tous les moteurs locaux, avec ses fichiers (les fichiers qu'un autre torrent lit encore sont conservés).
- Une interrogation échouée est ignorée et les interrogations continuent. L'échéance, 6 heures après le handoff, est stockée dans le job : un redémarrage du contrôleur reprend l'attente. Passé ce délai, le job échoue avec
the node did not report a full copy within 6 hours; the local copy is kept. - Si le nœud est retiré entre-temps, le job échoue avec
the node <name> was removed before it confirmed; the local copy is kept. Un renommage par Modifier (Edit) est suivi. - Annuler (Cancel) dans l'onglet Jobs arrête l'attente : les deux copies sont conservées.
- Un second déplacement du même torrent vers un nœud pendant qu'un autre attend est refusé (409) :
a move of this torrent to a node is already waiting; see Jobs.
sequenceDiagram
actor Admin
participant C as Contrôleur
participant N as Nœud
Admin->>C: Move to engine, choix de node-hoard
opt le torrent a une catégorie
C->>N: GET /api/categories
end
alt le nœud a des catégories, mais pas celle-ci
C-->>Admin: erreur listant les catégories du nœud
else catégorie connue, nœud sans catégorie, pas de catégorie, ou appel des catégories échoué
C->>N: POST /api/torrents/upload avec .torrent, catégorie, moteur
C->>N: POST /api/torrents/HASH/peers avec auto:PORT
C->>C: job handoff, état en attente, échéance dans 6 h
C-->>Admin: tâche groupée : envoyé, la copie locale part quand le nœud l'a (Jobs)
N->>N: auto = IP de l'appelant, connexion mise en file
N->>C: connexion BitTorrent au moteur du contrôleur
C-->>N: pièces par BitTorrent, vérifiées par hash sur le nœud
loop toutes les 30 s tant que le job attend, reprise après un redémarrage
C->>N: GET /api/engines/ENGINE/page?search=HASH
Note over C,N: une interrogation échouée est ignorée, les interrogations continuent
end
alt le nœud indique une progression de 1
C->>C: job terminé, retire le torrent de tous les moteurs locaux, avec ses fichiers
else échéance passée
C->>C: job échoué, copie locale gardée
else nœud retiré sur le contrôleur
C->>C: job échoué, copie locale gardée
else Annuler dans l'onglet Jobs
C->>C: job annulé, les deux copies gardées
end
end
Sélectionnez des lignes d'un seul nœud, puis Duplicate to engine › ou Move to engine › et choisissez local-<id> (bring it here). Le contrôleur :
- refuse si le moteur n'est pas un moteur local (400) ou si cette instance a déjà le torrent (409) ;
- télécharge le
.torrentdepuis le nœud (GET /api/torrents/<hash>/torrent) et lit le port d'écoute du moteur du nœud qui le détient (GET /api/engines) ; - ajoute le torrent au moteur local choisi, sans catégorie, et se connecte au nœud sur ce port.
Les données arrivent ensuite par BitTorrent. Un rapatriement ne retire jamais rien sur le nœud : retirez sa copie dans l'interface du nœud (Open) si vous le souhaitez.
Sur des lignes distantes, Move to engine › <node>-<engine> (there, no copy) demande au nœud de déplacer le torrent vers son autre moteur (POST /api/torrents/<hash>/engine sur le nœud). Rien ne traverse le réseau ; la réponse du nœud est transmise telle quelle.
Les mêmes opérations existent sous forme de routes sur le contrôleur : POST /api/nodes/:name/handoff ({info_hash, engine, category, then, from} ; avec then: "remove", la réponse porte l'identifiant du job), POST /api/nodes/:name/fetch ({info_hash, engine, from_engine, category}, from_engine vaut hoard par défaut), POST /api/nodes/:name/move-engine ({info_hash, engine}). Voir API REST.
Gestion des nœuds :
-
PATCH /api/nodes/:name: n'importe lesquels de{name, url, api_key}, au moins un (sinon 400nothing to change: send name, url and/or api_key). Répond{"status":"ok","name":…,"health":…}; 404 pour un nœud inconnu. -
POST /api/nodes/:name/rotate-key: répond{"status":"ok","name":…,"api_key":<new key>,"health":…}; 502 avec la raison quand le nœud ne change pas de clé. - Sur toute instance, les deux étapes d'une rotation :
POST /api/auth/api-key/rotate(clé actuelle dansX-Api-Key; répond{"pending_key", "expires_in": 600, "confirm"}), puisPOST /api/auth/api-key/confirmavec la clé en attente dansX-Api-Key. Elles changent aussi la clé d'une instance qui n'est pas un nœud (Sécurité & accès).
curl -X POST http://192.0.2.10:8199/api/nodes/seedboxde/handoff \
-H "X-Api-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"info_hash":"<40-hex info hash>","engine":"hoard","then":"keep"}'Ces éléments restent propres à chaque instance, même avec des nœuds déclarés :
- L'ajout de torrents : l'onglet + Ajouter un torrent (+ Add Torrent), les dossiers surveillés et le shim ajoutent uniquement aux moteurs locaux. Pour mettre un torrent sur un nœud depuis ici, faites le handoff d'un torrent que le contrôleur possède déjà.
- Les totaux de l'en-tête et de la Vue d'ensemble, les débits, les records et les chiffres de performances.
- L'onglet Race, l'onglet Trackers et les statistiques des trackers.
- Le shim qBittorrent (
/api/v2/*) : Sonarr, Radarr, autobrr et cross-seed ne voient que les torrents du contrôleur. Faites pointer chaque outil vers l'instance qui doit détenir ses torrents (Shim qBittorrent & automatisation). - Les workflows : ils s'exécutent sur l'instance où ils sont définis, sur ses propres torrents.
- Les jobs, le drain, les catégories et la configuration.
- MCP : les outils de lecture listent les lignes fusionnées, mais les outils d'écriture n'agissent que sur les torrents locaux (Serveur MCP).
- Le contrôleur détient la clé API de chaque nœud, en clair dans son store. Quiconque possède la clé API ou l'identifiant de l'interface du contrôleur peut agir sur tous les nœuds, et Open remet la clé d'un nœud à tout navigateur connecté au contrôleur. La carte d'enrôlement affirme que le contrôleur « never holds a credential » (ne détient jamais d'identifiant) pour le nœud : c'est faux en 4.4. Ce qui est vrai : le contrôleur n'obtient jamais de shell ni d'accès SSH au nœud. Si la clé ou le store du contrôleur fuit, changez la clé de chaque nœud avec Changer la clé (Rotate key), ainsi que la clé du contrôleur lui-même (Sécurité & accès).
-
/install.shet/api/nodes/registerne demandent pas de clé API ; register n'accepte qu'un jeton valide, non expiré et non utilisé (les jetons expirés restent dans le store, sans danger). - L'enrôlement et les appels aux nœuds passent en HTTP simple par défaut. Gardez les nœuds sur un réseau privé ou un VPN entre vos machines. Pour du HTTPS, placez le nœud derrière un reverse proxy et déclarez-le avec son URL
https://(voir la réserve sur le handoff plus haut). Voir Sécurité & accès. - Chaque nœud expose sa propre API sur le port 8199 (
api_host = "0.0.0.0"avec le script d'enrôlement). Protégez-le par un pare-feu comme n'importe quelle autre instance.
- Topologies de déploiement : quand plusieurs machines en valent la peine.
- Moteurs : race & hoard : les moteurs à l'intérieur d'une instance, les moteurs supplémentaires.
- Jobs, actions groupées & déplacement : tâches groupées, jobs, déplacements de données.
- Docker et Linux (bare metal) : installer vous-même un nœud avant de le déclarer.
- Limites connues de la 4.4 : tous les manques dans une seule liste.