Documentation J+SERVICES Guides Référence API

Contrat API — Maîtrise de consommation (bridage)

Base : https://live.jmoai.net/api/v1/radius. Authentification : JWT client + licence RADIUS active + abonnement Suivi du Matériel actif, sauf mention contraire. La licence annuelle STARLINK X MikroTik donne la capacité RADIUS/MikroTik ; le suivi mensuel donne l’accès au bridage, à son journal et à ses mesures.

Ce que fait le service

Le gérant décrit une intention ; le backend l’applique et la maintient sur son routeur, en continu, sans que l’application ait besoin d’être ouverte. Trois leviers se combinent :

Levier Déclencheur Effet
Bridage programmé dates ou jours de semaine choisis plafond global sur le routeur
Bridage adaptatif quota atteint ou pic de consommation détecté même plafond, hors jours prévus
Bridage ciblé un usager dépasse son quota individuel ce seul usager est ralenti, sur tous ses appareils

Les deux populations d’usagers

Le service fonctionne à l’identique sur un routeur piloté par RADIUS et sur un routeur dont les utilisateurs sont créés en local par l’application — ce sont deux moitiés du parc, et aucune n’est traitée comme un cas particulier.

Un usager vu des deux côtés — cas normal d’un hotspot RADIUS — n’est pas compté deux fois : la clé de dédoublonnage est le couple (usager, adresse), et c’est le plus grand des deux volumes qui est retenu, jamais leur somme. Le champ sources de reason indique d’où vient la mesure retenue.

Si aucune des deux sources ne répond, les bridages ciblés en cours sont laissés en l’état. Traiter ce silence comme « plus personne ne consomme » libérerait tout le monde au premier incident réseau.

Rien n’est jamais bloqué ni déconnecté. Sous bridage, le trafic essentiel (messagerie, réseaux sociaux, DNS) garde une priorité haute et un débit garanti ; tout le reste passe en priorité basse. Hors congestion, ce « reste » atteint quand même le plafond : la priorité ne se manifeste qu’au moment où le tuyau est plein. Un client qui consulte WhatsApp ne verra pas la différence entre un jour bridé et un jour normal ; celui qui télécharge la verra.

GET /bandwidth/classes

Le catalogue des classes de trafic préservées, pour l’écran de réglage. JWT seul, pas de licence requise.

{
  "classes": [
    { "id": "dns", "libelle": "Résolution DNS", "plages": 5 },
    { "id": "meta", "libelle": "WhatsApp, Messenger, Instagram, Facebook", "plages": 12 },
    { "id": "telegram", "libelle": "Telegram", "plages": 8 },
    { "id": "x", "libelle": "X (Twitter)", "plages": 3 }
  ]
}

⚠️ La classification se fait par adresse IP. Elle attrape la signalisation, les messages et les API, pas toujours la vidéo, qui est servie depuis des CDN mutualisés. C’est voulu : la vidéo d’un réseau social est de la consommation lourde et a sa place dans la file basse. TikTok et Snapchat sont volontairement absents pour la même raison — les inscrire reviendrait à prioriser au hasard tout ce qui partage leurs CDN.

PUT /bandwidth/program

Enregistre ou remplace le programme d’un routeur. Quota : 5 appels/minute et par utilisateur.

{
  "nas_id": "uuid",
  "enabled": true,
  "mode": "BOTH",
  "limit_mbps": 10,
  "essential_share": 0.25,
  "essential_classes": ["dns", "meta", "telegram", "x"],
  "scheduled_dates": ["2026-09-05", "2026-09-12"],
  "schedule_weekdays": [2, 3],
  "daily_quota_gb": 80,
  "monthly_quota_gb": 1500,
  "user_daily_quota_gb": 5,
  "user_limit_mbps": 1,
  "max_throttled_users": 10
}
Champ Obligatoire Règle
nas_id oui routeur du client authentifié
limit_mbps oui > 0 et ≤ 10000
mode non SCHEDULED, ADAPTIVE ou BOTH (défaut)
essential_share non strictement entre 0 et 1 (défaut 0,25)
essential_classes non identifiants de GET /bandwidth/classes ; les inconnus sont ignorés
scheduled_dates non YYYY-MM-DD, dédoublonnées et triées par le serveur
schedule_weekdays non entiers 0–6, 0 = dimanche
daily_quota_gb, monthly_quota_gb non > 0 ; déclenchent le mode adaptatif
user_daily_quota_gb + user_limit_mbps non vont ensemble ou pas du tout ; quota jugé sur le total de l’usager, tous appareils confondus (jusqu’à 16 adresses par file)
max_throttled_users non entier 0–100 (défaut 10)

Réponse 200 :

{ "nas_id": "uuid", "enabled": true, "mode": "BOTH", "revision": 3, "application": "PLANIFIEE" }

application: "PLANIFIEE" est un engagement, pas une esquive : la pose sur le routeur a lieu à la réconciliation suivante (au plus 5 minutes). Faire échouer l’enregistrement parce que le routeur est hors ligne priverait le gérant de la seule chose qu’il maîtrise — son intention — et l’obligerait à réessayer sans savoir quand. L’état réellement constaté se lit sur GET /bandwidth/program.

Codes d’erreur

Code Sens
BW_NAS_REQUIRED nas_id absent
BW_NAS_NOT_OWNED le NAS n’appartient pas au client authentifié
BW_LIMIT_REQUIRED / BW_LIMIT_INVALID plafond absent ou hors bornes
BW_MODE_INVALID mode inconnu
BW_SCHEDULE_EMPTY mode SCHEDULED sans aucune date ni jour — ne briderait jamais
BW_DATE_INVALID / BW_WEEKDAY_INVALID format de date ou jour hors 0–6
BW_SHARE_INVALID essential_share hors ]0, 1[
BW_CLASSES_INVALID aucune classe connue dans la liste fournie
BW_USER_PAIR_REQUIRED quota individuel et plafond individuel séparés
BW_MAX_USERS_INVALID max_throttled_users hors 0–100
CLICK_SPAM_PROTECTION quota de 5 appels/minute dépassé

GET /bandwidth/program

L’état de tous les routeurs du client.

{
  "programmes": [
    {
      "nas_id": "uuid",
      "enabled": true,
      "mode": "BOTH",
      "limit_mbps": 10,
      "etat_souhaite": "ON",
      "etat_constate": "ON",
      "timezone": "Africa/Porto-Novo",
      "derniere_verification": "2026-08-29T14:32:11Z",
      "en_erreur": false,
      "jours_programmes_restants": 2,
      "consommation": {
        "octetsJour": 64424509440,
        "octetsDeuxHeures": 8589934592,
        "octetsMois": 1073741824000,
        "dernierReleve": "2026-08-29T14:30:00Z"
      },
      "usagers_brides": 2
    }
  ],
  "usagers_brides": [
    {
      "nas_id": "uuid",
      "username": "TM-ABC123",
      "limit_mbps": 1,
      "framed_ips": ["10.5.0.23", "10.5.0.30"],
      "observed_bytes": 12884901888,
      "applied_at": "2026-08-29T13:05:00Z"
    }
  ]
}

Points d’attention pour l’affichage :

DELETE /bandwidth/program/{nasId}

Désactive le programme et nettoie le routeur : plan global et bridages ciblés sont retirés. Quota : 5 appels/minute.

{ "disabled": true, "nettoye": true }

nettoye: false signifie que le routeur était injoignable. La désactivation est enregistrée quand même — le gérant a demandé l’arrêt, il l’obtient — et un balayage de nettoyage retirera les files au retour du routeur. Sans ce balayage, un client resterait bridé indéfiniment après avoir demandé l’arrêt.

GET /bandwidth/activity

Ce que le système a fait, raconté en français. C’est l’écran « activité » de l’application.

Paramètres : limite (1–200, défaut 50), avant (curseur), nas_id (filtre facultatif).

{
  "activites": [
    {
      "id": "uuid",
      "nas_id": "uuid",
      "type": "THROTTLE_ON",
      "date": "2026-08-29T18:04:12Z",
      "texte": "Bridage activé — pic de consommation détecté (+120 % sur 2 h).",
      "detail": { "motif": "PIC_DE_CONSOMMATION", "mesures": { "acceleration_pct": 120 } }
    },
    {
      "id": "uuid",
      "nas_id": "uuid",
      "type": "USER_THROTTLED",
      "date": "2026-08-29T18:04:15Z",
      "texte": "TM-C4D835 ralenti à 1 Mbps — 19.9 Go consommés sur 6 appareils.",
      "detail": { "username": "TM-C4D835", "observe_gb": 19.9, "adresses": 6 }
    }
  ],
  "suivant": "2026-08-29T18:04:15Z"
}

GET /bandwidth/impact

Ce que le bridage a changé. C’est l’écran qui justifie la fonction auprès du gérant.

Paramètres : jours (7–180, défaut 30), nas_id (facultatif).

{
  "fenetre_jours": 30,
  "programmes": 1,
  "timezone": "Africa/Porto-Novo",
  "impact": {
    "courbe": [
      { "jour": "2026-08-01", "gb": 98.4, "bride": false },
      { "jour": "2026-08-02", "gb": 61.2, "bride": true }
    ],
    "jours_brides": 8,
    "jours_normaux": 21,
    "moyenne_jour_bride_gb": 61.2,
    "moyenne_jour_normal_gb": 98.4,
    "reduction_pct": 38,
    "economie_estimee_gb": 297.6,
    "fiabilite": "BONNE",
    "fiabilite_motif": null,
    "total_gb": 2451.8
  },
  "usagers": {
    "distincts": 12,
    "interventions": 34,
    "actuellement_brides": 2,
    "volume_observe_gb": 412.7
  }
}

Comment la réduction est calculée — methode

Depuis le 2026-08-29, la comparaison apparie les jours de la semaine : samedi contre samedi, mardi contre mardi. La consommation d’un hotspot est fortement saisonnière à la semaine, et l’on bride justement les jours chargés — comparer la moyenne des jours bridés à celle des jours normaux produisait donc un biais systématiquement flatteur.

Champ Sens
methode: "JOURS_APPARIES" samedi comparé à samedi. Chiffre à afficher.
methode: "MOYENNE_BRUTE" aucun jour de semaine n’avait les deux sortes de jours — repli assumé
jours_semaine_apparies combien de jours de semaine ont pu être appariés (2 minimum)
reduction_brute_pct l’ancien calcul, exposé À CÔTÉ

Un écart important entre reduction_pct et reduction_brute_pct n’est pas une anomalie : il mesure le poids de la saisonnalité. Affichez reduction_pct ; gardez l’autre pour un écran de diagnostic.

⚠️ La règle d’affichage la plus importante de ce contrat

Quand fiabilite vaut INSUFFISANTE, n’affichez NI reduction_pct NI economie_estimee_gb — ils valent alors null, et fiabilite_motif porte la phrase à montrer à la place (« il faut au moins 3 jours bridés et 3 jours normaux pour comparer (actuellement 1 et 2) »).

Comparer des jours bridés à des jours normaux est une corrélation, pas une preuve : la consommation varie d’elle-même — week-ends, congés, un gros client qui part. Avec deux jours d’un côté et trois de l’autre, un écart de 40 % est du bruit. Un chiffre flatteur et faux détruit la confiance le jour où le gérant s’en aperçoit ; un chiffre modeste et honnête la construit. C’est pourquoi le serveur refuse de conclure plutôt que de renvoyer un zéro.

reduction_pct peut être négatif : les jours bridés ont alors consommé plus que les normaux. Affichez-le tel quel. Le masquer donnerait un tableau de bord qui ne sait dire que de bonnes nouvelles, donc auquel personne ne croira.

economie_estimee_gb est une estimation, jamais une mesure : on ne sait pas ce qui aurait été consommé sans bridage. Le libellé côté application doit le dire.

Autres points

Notifications

Le gérant reçoit une notification (base + push FCM) sur les transitions uniquement : bridage activé, bridage levé, pic détecté, routeur injoignable. Elles portent metadata.type = "BANDWIDTH" et metadata.nas_id.

Ce qui ne déclenche pas de notification, volontairement : le bridage d’un usager individuel. Ils sont fréquents, et une notification par personne noierait le gérant — le journal d’activité les porte tous.

Réglages, dans qoe_notification_preferences (mêmes heures de silence que les rapports QoE, pour ne pas obliger le gérant à les régler deux fois) :

Champ Effet
bandwidth_push coupe toutes les notifications de bridage
quiet_start / quiet_end heures de silence, respectées sauf pour un routeur injoignable

Une même alerte n’est pas répétée avant 6 heures pour un routeur donné. La réconciliation passant toutes les 5 minutes, une fenêtre plus courte que cette cadence ne dédoublonnerait rien : c’est le mécanisme exact qui a produit 330 alertes en 48 h et fait tomber les messages destinés aux clients.

GET /admin/bandwidth/fleet (administration)

{
  "programmes": { "total": 842, "actifs": 610, "en_bridage": 47, "injoignables": 23 },
  "usagers_actuellement_brides": 118,
  "evenements_24h": { "THROTTLE_ON": 51, "USER_THROTTLED": 204, "ROUTER_UNREACHABLE": 12 },
  "evenements_24h_tronques": false
}

evenements_24h_tronques: true signifie que la répartition est calculée sur les 1 000 premiers événements seulement. Affichez-le : une répartition tronquée présentée comme un total est un chiffre faux.

POST /admin/bandwidth/reconcile (administration)

Force un passage de réconciliation sans attendre le cron. ?limite=1..200, défaut 40. Sert à valider un programme en production immédiatement après l’avoir enregistré.

{
  "traites": 40,
  "poses": 3,
  "bascules": 1,
  "conformes": 5,
  "inchanges": 30,
  "injoignables": 1,
  "usagers_brides": 2,
  "usagers_liberes": 0
}

inchanges est le chemin économe : la décision n’a pas bougé et aucune session n’a été ouverte vers ces routeurs. C’est ce qui rend le balayage tenable sur plusieurs milliers d’appareils.

Adresse MAC de substitution — starlink_x

📱 Dev de l’app mobile : la note d’intégration pas à pas est sur /docs/CONTRAT-API-ANTI-RESTRICTION-APP — ordre de pose, vérifications, recette. Ce chapitre-ci reste la référence des routes.

C’est l’application qui pose l’adresse, depuis le réseau local du client. Le backend ne la pose jamais.

Ce n’est pas une limitation temporaire. Changer la MAC de l’interface WAN renégocie le bail DHCP : si le fournisseur lie la session à l’adresse — ce que fait la parabole Starlink — le lien tombe. Le tunnel de gestion et la voie de secours Back To Home empruntent ce même lien : le filet de sécurité disparaîtrait en même temps que la panne qu’il devait rattraper, et plus rien ne pourrait déclencher un retour en arrière. Depuis le LAN, l’opérateur garde un chemin vers l’appareil quoi qu’il arrive au WAN. /interface ethernet reste donc un chemin sensible de routerWriteGuard, sans catalogue : une tentative de pose côté serveur est refusée.

Le backend garantit trois choses : l’adresse est déterministe (dérivée du numéro de série, donc identique d’une consultation à l’autre), l’adresse d’origine est enregistrée avant d’être remplacée, et chaque geste entre dans le journal GET /bandwidth/activity (MAC_SUBSTITUTED, MAC_RESTORED, MAC_POSE_FAILED).

Masquer la MAC ne suffit pas

Un routeur MikroTik s’annonce sur son lien par quatre signaux. L’adresse n’en est qu’un, et ce n’est pas le plus bavard :

Signal Ce que le fournisseur voit Traité ?
Adresse MAC « fabricant inconnu » mac_substitution
Découverte de voisinage (MNDP/CDP/LLDP) constructeur, version de RouterOS et identité du client, en clair ✅ limitée au LAN
Nom d’hôte DHCP (option 12) RouterOS envoie l’identité système dans sa demande de bail ✅ retiré ou remplacé
Empreinte DHCP (option 55) ordre des options demandées, caractéristique de RouterOS ❌ ne se change pas

La quatrième ligne est à dire au gérant. Un camouflage annoncé comme total et qui ne l’est pas est pire qu’un camouflage partiel assumé : il ferait prendre des risques qu’on croit couverts.

On restreint la découverte, on ne la coupe pas : elle reste active sur les interfaces locales, sans quoi un technicien ne retrouverait plus le routeur dans WinBox depuis le LAN — c’est ainsi qu’on le dépanne quand tout le reste est tombé.

Et l’on ne renomme pas le routeur : /system identity reste le vrai nom, celui que le technicien lit. Le nom d’hôte envoyé au fournisseur est une option DHCP séparée.

GET /starlink/mac/{nasId}/consigne

Paramètre Rôle
interface l’interface WAN, telle que l’application la lit sur le routeur
interfaces_locales les interfaces où la découverte reste permise, séparées par des virgules
nom_hote nom d’hôte de substitution ; absent = aucun nom d’hôte envoyé

Sans interface, la commande n’est pas délivrée : le serveur ne devine pas quelle interface porte le WAN, et se tromper poserait l’adresse sur le LAN. Sans interfaces_locales, le bloc discretion vaut null avec discretion_motif: "INVENTAIRE_INTERFACES_MANQUANT".

Le cas normal : nommer le pont

Les gérants de hotspot relient leurs ports sous un pont unique, sur lequel tourne le serveur hotspot. Il suffit alors de nommer ce pont : interfaces_locales=bridge-hotspot. Inutile d’énumérer ether2, ether3, wlan1 — la découverte sur un pont atteint tous ses membres.

Et c’est aussi la limite : un pont ne se découpe pas. Un pont est un domaine de diffusion unique ; on ne peut pas activer la découverte sur ether2 mais pas sur ether3 quand les deux en sont membres. C’est le pont entier, ou rien.

Conséquence concrète, que l’application doit dire au gérant : découverte sur le pont du hotspot = les clients payants voient « MikroTik, RouterOS 7.19.6, nom du gérant » dans WinBox. Le serveur le signale de lui-même dans la réponse :

"discretion": {
  "portee": "PONT",
  "avertissement": "La découverte restera visible de tout ce qui est relié à bridge-hotspot — y compris les clients de votre hotspot. Pour ne la garder que pour vous, nommez une interface de gestion hors du pont."
}

portee vaut PONT, INTERFACES_NOMMEES ou AUCUNE. Affichez avertissement quand il n’est pas null — c’est le seul moment où le gérant peut encore choisir une interface de gestion hors du pont.

⚠️ L’inventaire vient de l’application, et c’est délibéré. Le serveur ne connaît pas la topologie du routeur. Sur un routeur dont le WAN est membre d’un pont, mettre ce pont dans les interfaces locales ferait ressortir la découverte par le pont, donc par le WAN — le contraire du but. N’incluez pas un pont qui contient l’interface WAN. Le serveur écarte malgré tout l’interface WAN de la liste, par sécurité.

{
  "nas_id": "uuid",
  "identifiant": "HH40AF7YVNP",
  "mac_substitution": "02:AB:CD:EF:01:02",
  "interface": "ether1",
  "commande": "/interface ethernet set [find name=ether1] mac-address=02:AB:CD:EF:01:02",
  "commande_retour": null,
  "pose_par": "APP_LAN",
  "precautions": ["À exécuter depuis le réseau local du routeur. …"],
  "discretion": {
    "commandes": [
      ":if ([:len [/interface list find name=JSLOCAL]] = 0) do={/interface list add name=JSLOCAL comment=JSLOCAL}",
      ":if ([:len [/interface list member find list=JSLOCAL interface=ether2]] = 0) do={/interface list member add list=JSLOCAL interface=ether2}",
      "/ip neighbor discovery-settings set discover-interface-list=JSLOCAL",
      "/ip dhcp-client set [find interface=ether1] dhcp-options=clientid"
    ],
    "interfaces_locales": ["ether2", "wlan1"],
    "nom_hote": null,
    "limites": ["L’empreinte DHCP de RouterOS reste reconnaissable : elle ne se change pas.", "…"]
  },
  "discretion_motif": null,
  "discretion_appliquee": false,
  "etat": null,
  "droit": { "autorise": false, "motif": "STARLINK_X_REQUIRED", "enforced": false }
}

POST /starlink/mac/{nasId}

Ce que l’application déclare avoir fait. Le serveur ne peut pas le vérifier — le routeur est peut-être injoignable précisément parce que la pose a réussi.

{
  "resultat": "POSEE",
  "interface": "ether1",
  "mac_origine": "48:A9:8A:11:22:33",
  "mac_posee": "02:AB:CD:EF:01:02",
  "internet_confirme": true,
  "discretion_appliquee": true,
  "interfaces_locales": ["ether2", "wlan1"],
  "nom_hote": null,
  "motif": null
}

discretion_appliquee: true exige interfaces_locales : un registre qui affirme sans rien prouver ne servirait pas le support, qui doit pouvoir répondre à « ce routeur crie-t-il encore son nom ? » sans aller le voir. L’événement WAN_DISCRETION_APPLIED entre dans le journal.

Réponse immédiate — l’application enchaîne sur l’écran de suivi sans second appel :

{
  "nas_id": "uuid",
  "etat": "POSEE",
  "commande_retour": "/interface ethernet set [find name=ether1] mac-address=48:A9:8A:11:22:33",
  "service_etat": "INITIALISATION",
  "titre": "Service Anti-Restriction en cours d’initialisation…"
}

mac_origine est exigée même quand resultat vaut ECHEC : c’est justement quand la pose a mal tourné qu’il faut savoir vers quoi revenir. resultat vaut POSEE, RESTAUREE ou ECHEC ; un ECHEC ne porte pas de mac_posee.

Code Sens
MAC_RESULT_INVALID resultat hors des trois valeurs
MAC_INTERFACE_INVALID interface absente ou non nommable
MAC_ORIGIN_INVALID mac_origine absente ou mal formée
MAC_POSED_NOT_UNICAST adresse de groupe — une carte ne peut pas la porter en source
MAC_NO_EFFECT adresse posée identique à l’origine : le lien tomberait sans rien masquer
MAC_MISMATCH l’adresse déclarée n’est pas celle calculée pour ce routeur
MAC_SERIAL_UNKNOWN numéro de série inconnu — l’adresse ne peut pas être dérivée

Le parcours du bouton, côté application

Le bouton « Activer l’Anti-Restriction » est un déclencheur, pas un interrupteur. Il ne bascule pas un réglage : il lance une séquence dont chaque étape a une durée réelle. L’écran doit suivre cette séquence, sinon le gérant conclura à une panne pendant la minute où tout se passe normalement.

1. GET  /starlink/mac/{nasId}/consigne?interface=ether1   → l'adresse et la commande
2. (application, sur le LAN)  pose la commande sur le routeur
3. (application)  attend le retour d'Internet et le VÉRIFIE
4. POST /starlink/mac/{nasId}   { resultat: "POSEE", internet_confirme: true }
       ← réponse immédiate : service_etat "INITIALISATION"
       ← notification push : « Service Anti-Restriction en cours d'initialisation… »
5. GET  /starlink/mac/{nasId}/statut   (toutes les 5 s, tant que INITIALISATION)
       ← étapes : ROUTEUR, SAUVEGARDE, PROGRAMME
6. quand service_etat = "OPERATIONNEL"
       ← notification push : « Système Anti-Restriction actif sur SN…… »

internet_confirme n’est pas une formalité. C’est lui qui déclenche la reprise en main. Ne le posez qu’après avoir constaté, depuis le LAN, que le routeur a retrouvé Internet — un true optimiste ferait démarrer la séquence sur un routeur encore en train de renégocier son bail DHCP, et le gérant lirait un échec là où il n’y avait qu’une attente.

Les deux moments sont séparés parce qu’ils le sont vraiment. Entre la pose et le service opérationnel, le bail se renégocie, le tunnel remonte, la sauvegarde se rapatrie, le programme se pose. Afficher « actif » dès l’étape 4 mentirait pendant la seule minute où quelque chose peut encore mal tourner — et c’est la minute où le gérant regarde son écran.

GET /starlink/mac/{nasId}/statut

Lecture ouverte, sans licence ni quota : c’est l’écran qui suit le bouton, il est interrogé en boucle, et le couper laisserait une barre qui tourne sans jamais conclure.

{
  "nas_id": "uuid",
  "router_serial": "HH40AF7YVNP",
  "service_etat": "INITIALISATION",
  "titre": "Service Anti-Restriction en cours d’initialisation…",
  "etapes": [
    { "cle": "ROUTEUR", "libelle": "Le routeur répond de nouveau", "etat": "OK" },
    { "cle": "SAUVEGARDE", "libelle": "Sauvegarde de la configuration", "etat": "EN_ATTENTE" },
    { "cle": "PROGRAMME", "libelle": "Maîtrise de consommation en place", "etat": "EN_ATTENTE" }
  ],
  "demarre_le": "2026-08-29T07:31:02Z",
  "operationnel_depuis": null,
  "peut_relancer": false
}
service_etat Ce que l’application affiche
EN_ATTENTE le bouton d’activation
INITIALISATION « Service Anti-Restriction en cours d’initialisation… » + les étapes
OPERATIONNEL « Système Anti-Restriction actif sur SN…… », avec operationnel_depuis
DEGRADE l’adresse est posée mais la reprise n’a pas abouti — bouton « Réessayer »

POST /starlink/mac/{nasId}/relancer

Le bouton « Réessayer ». Sous licence, comme toute action. Rend le même objet que /statut, avec relance: true si la reprise a bien redémarré. Relancer une reprise déjà en cours et non expirée renvoie relance: false : deux sauvegardes concurrentes sur le même routeur ne serviraient à rien.

Notifications

Deux poussées, de type STARLINK_X, en plus de l’écran :

event_type Texte
SERVICE_INITIALIZING Service Anti-Restriction en cours d’initialisation…
SERVICE_READY Système Anti-Restriction actif sur SN……
SERVICE_DEGRADED L’adresse est posée, mais le routeur n’a pas encore répondu au serveur

Elles ne respectent pas les heures silencieuses : c’est le gérant qui vient d’appuyer sur le bouton, il attend la réponse à l’heure qu’il est. Le dédoublonnage se fait par activation (la clé porte l’horodatage de début), donc une relance après un échec notifie de nouveau — sans quoi le gérant croirait que rien ne s’est passé.

Les trois événements entrent aussi dans GET /bandwidth/activity, avec la même phrase.

GET /starlink/mac

Les routeurs masqués du client. Lecture ouverte, sans licence : un gérant dont la licence a expiré doit pouvoir constater quelle adresse porte son routeur, et la restaurer.

Ce que le backend écrit sur le routeur

Uniquement /queue simple et /queue type, sous le commentaire JSBRIDAGE.

Le plan compte une file par plage préservée, plus un parent et une file « reste » — soit 25 à 30 files selon les classes choisies dans essential_classes. Ce n’est pas un choix de conception : sur RouterOS 7, le paramètre dst d’une file simple n’accepte qu’une seule valeur. Les files sont posées en un seul aller-retour groupé (mesuré : 2,4 s pour 28 files), partagent toutes le même commentaire, et se retirent en une commande. Jamais de /system script, /system scheduler, /ip firewall filter ni /ip firewall nat : ces chemins sont refusés par routerWriteGuard, et le calendrier vit donc dans le backend plutôt que dans un planificateur embarqué. Une file mal réglée ralentit ; elle ne peut pas rendre un routeur injoignable — c’est ce qui autorise le backend à écrire ici sans humain sur le LAN.

Le retrait ne vise jamais que le commentaire JSBRIDAGE : aucune file du client, du hotspot ou d’un revendeur n’est touchée.