Contrat API — Ports d’accès distant & partage de passerelle
Destinataire : développeur de l’application client (mobile)
Base : https://live.jmoai.net · auth Authorization: Bearer <jwt client>
Enveloppe : { success: boolean, data: …, error?: { code, message } }
Déployé en production le 2026-08-10. Le paramètre
port, le champaccess_modeet la notificationDELEGATED_ROUTER_ACCESSsont actifs.
1. Le principe
Un seul appel renvoie tous les ports d’un routeur. L’application n’a jamais besoin de demander « le port SSH » séparément : elle récupère la matrice entière et y pioche.
Deux familles de ports coexistent, et les confondre est l’erreur la plus fréquente :
App ──────► vpn.mikhmoai.com : 23000 ──────► routeur : 22
└── vpnRedirection.ssh_port └── local_ports.ssh
(port PUBLIC sur le relais) (port NATIF du routeur)
vpnRedirection= ce que l’application compose pour se connecter.local_ports= ce qui tourne réellement sur le routeur. Ils fixent le schéma d’URL : le relais est un flux TCP brut qui ne termine aucun TLS, donc c’est le port natif qui décide entrehttp://ethttps://.
2. GET /api/v1/clients/remote-access/ports
Requête
| Paramètre | Requis | Rôle |
|---|---|---|
router_serial |
✅ | numéro de série du routeur (alias acceptés : router_sn, sn) |
port |
— | un port de redirection actif du routeur. Requis uniquement en accès partagé (§3). Alias : current_port, active_port |
GET /api/v1/clients/remote-access/ports?router_serial=HXX0EXEMPLE01
GET /api/v1/clients/remote-access/ports?router_serial=HXX0EXEMPLE01&port=17000
Réponse — succès
{
"success": true,
"data": {
"found": true,
"router_serial": "HXX0EXEMPLE01",
"allocation_id": "…",
"access_mode": "OWNER", // ou "DELEGATED" — voir §3
"online": true,
"ports_active": true, // tunnel EN LIGNE *et* relais publié
"tunnel_status": "CONNECTED",
"health_status": "…",
"relay_status": "PUBLISHED",
"last_seen_at": "2026-08-10T…Z",
"vpnRedirection": {
"public_host": "vpn.mikhmoai.com",
"webfig_port": 17000,
"rest_port": 17000, // ⚠️ identique à webfig_port — voir §5
"winbox_port": 19000,
"api_port": 22000,
"ssh_port": 23000,
},
// ⚠️ Valeurs de CONVENTION, pas une mesure du routeur — voir §5
"local_ports": { "webfig": 8081, "winbox": 8291, "api": 8728, "ssh": 22 },
"urls": {/* URLs prêtes à ouvrir */},
},
}
Réponse — routeur inconnu ou non autorisé
HTTP 200, pas une erreur :
{
"success": true,
"data": {
"found": false,
"online": false,
"router_serial": "…",
"message": "Aucun équipement provisionné pour ce numéro de série.",
},
}
Erreur
| Code | HTTP | Cause |
|---|---|---|
ROUTER_SERIAL_REQUIRED |
400 | router_serial absent ou vide |
3. Partage de passerelle (QR) — l’accès délégué
Le problème résolu
Quand un client partage sa passerelle avec un technicien, l’application ne transmet ni compte ni adresse e-mail : seulement un hôte, un port et des identifiants. Côté serveur, le technicien n’est propriétaire d’aucun équipement — l’appel lui renvoyait donc une matrice vide, et l’application ne pouvait proposer ni SSH, ni SFTP, ni mise à jour du portail captif.
Ce que l’application doit faire
Joindre le port qu’elle détient déjà :
GET /remote-access/ports?router_serial=<SN>&port=<le port reçu par le partage>
Le couple (numéro de série + port actif) vaut preuve d’accès : c’est exactement ce que le partageur a remis volontairement.
Règle d’implémentation : envoyez port dès que vous l’avez, sans chercher à
deviner si l’utilisateur est propriétaire. Le paramètre est ignoré quand il l’est.
Lire access_mode
| Valeur | Sens | Usage suggéré |
|---|---|---|
OWNER |
le routeur est dans le parc de l’utilisateur | affichage normal |
DELEGATED |
accès obtenu par partage | signaler « accès partagé » dans l’interface |
4. Ce que le serveur garantit
- L’appel reste authentifié. Le port prouve la légitimité, jamais l’identité : celle-ci vient du jeton, jamais d’un champ de la requête. Un technicien ne peut pas se déclarer être quelqu’un d’autre.
- Aucune fuite latérale. Un accès délégué ne divulgue que les autres ports de la même allocation. Jamais un identifiant, jamais un autre routeur.
- Le propriétaire est prévenu. Il reçoit une notification
DELEGATED_ROUTER_ACCESS: « Adam a demandé les accès de votre hAP ax³ », avec le nom du technicien et celui du routeur dans les métadonnées. Fenêtre de silence de 6 h par couple technicien × routeur, pour qu’un après-midi de configuration ne produise pas cinquante notifications. - L’accès est journalisé côté serveur (technicien, propriétaire, allocation, port).
L’application n’a donc rien à réimplémenter de ces protections.
5. Pièges à ne pas contourner
-
rest_portESTwebfig_port. Sur RouterOS v7, l’API REST (/rest) est servie par le même servicewwwque WebFig : il n’existe pas de redirection REST distincte. Si l’application cherche un port REST séparé, elle cherche un port qui n’existe pas. -
native_port(dans/remote-access/redirections) est un alias d’api_port. -
local_portsdécrit une convention, pas l’état réel du routeur. Ces valeurs sont celles que le provisioning vise ; elles ne sont pas relevées sur l’équipement. Le servicewwwd’un routeur peut légitimement écouter sur un autre port : 8081 quand le provisioning a abouti, 80 sur une configuration d’usine ou après le passage d’un script de maintenance tiers. Relevé du 2026-08-16 sur 86 routeurs au tunnel actif : les deux cas coexistent dans le parc.Conséquence pour l’application : ne jamais composer une adresse à partir de
local_ports. L’entrée qui fait foi estpublic_host:<port>devpnRedirection— la plateforme s’occupe d’atteindre le bon port local, et c’est le seul chemin qui traverse l’overlay.local_portsn’a qu’une valeur de diagnostic, à afficher éventuellement dans un écran technique, jamais à utiliser pour se connecter. -
ports_active: falsen’est pas une panne : le tunnel est en ligne mais le relais n’est pas encore publié. Afficher « connexion en préparation », pas une erreur. -
found: falsen’est pas une erreur non plus : soit le numéro de série est inconnu, soit le port fourni ne correspond pas à ce routeur. Même écran dans les deux cas — ne distinguez pas, cela renseignerait un tiers sur l’existence d’un équipement.
6. Quota — à lire avant d’implémenter la boucle de rafraîchissement
100 requêtes / 15 min par IP, partagées par TOUTES les routes /api.
Ce n’est pas théorique : une vue qui sondait une route toutes les 15 secondes a consommé
à elle seule la totalité du quota, et ce sont les autres appels — licence, VPN — qui
ont échoué en 429. La route fautive, elle, semblait fonctionner.
Pour cet endpoint :
- interrogez à l’ouverture de l’écran, pas en boucle ;
- rafraîchissez sur action explicite de l’utilisateur ;
- une seule sonde à la fois — annulez la précédente avant d’en lancer une nouvelle ;
- la matrice de ports change rarement : gardez-la en cache pour la session, ne redemandez pas un port isolé.
7. Voir aussi
- Référence complète des routes :
https://live.jmoai.net/api-docs - Source Markdown de ce guide :
https://live.jmoai.net/docs/CONTRAT-API-ACCES-DISTANT-PORTS/raw