SYSTEM NOMINAL · UTC 2026-06-21T16:34:28Z · LAT 49.44649°N LON 1.09760°E · ALT 65 m

AG-05 · Embedded Runtime

nova-agent — Binaire embarqué

Le pont logiciel entre la plateforme cloud NOVA et le mini-PC qui pilote le télescope. Conçu pour être sûr par défaut, minimal, auditable et installable en 5 minutes par un non-développeur.

En une phrase

nova-agent est un binaire Rust statique de ~5 Mo qui tourne sur le mini-PC MeLE, établit une connexion HTTPS sortante toutes les 30 secondes vers nova-obsrouen.pages.dev, et exécute des commandes ASCOM (slew, capture, focus) uniquement si l'opérateur de l'observatoire a explicitement activé le mode pilotage.

Pourquoi un agent embarqué

Le mini-PC de l'observatoire est derrière une box ADSL/Fibre résidentielle ou la box de l'association — pas d'IP publique, pas de port forwarding, souvent sans VPN. Trois architectures envisageables :

  1. Pas d'agent — l'utilisateur prépare uniquement, l'opérateur exécute manuellement. Plus simple mais plus lent.
  2. VPN entrant (WireGuard, ZeroTier) — permet le SSH/RDP direct, mais demande à l'observatoire d'ouvrir un tunnel permanent. Risque sécuritaire.
  3. Agent sortant pullchoix NOVA — l'observatoire n'ouvre aucun port, l'agent poll la queue cloud. Zéro surface d'attaque entrante.

L'option 3 est devenue le standard de facto pour les agents distants (Tailscale Funnel, Cloudflare Tunnel, GitHub Actions self-hosted runners) : l'observatoire reste maître de sa connexion sortante, qu'il peut couper d'un clic.

Installation — Windows 10/11

Pré-requis

  • Windows 10 (1909+) ou Windows 11
  • ASCOM Platform 6.6+ installée (depuis ascom-standards.org)
  • Drivers ASCOM : Atik, ZWO, Robofocus, Temma déjà installés et testés via ASCOM Diagnostics
  • Connexion Internet sortante autorisée vers nova-obsrouen.pages.dev:443
  • ~50 Mo libres dans C:\nova-agent\

Étape 1 — Télécharger

Décompresser nova-agent-windows-x64.zip dans C:\nova-agent\ (créer le dossier si nécessaire). Contenu après extraction :

C:\nova-agent\
  ├── nova-agent.exe            ← binaire principal (5 Mo)
  ├── nova-agent-config.toml    ← configuration éditable
  ├── README.txt                ← démarrage rapide
  ├── LICENSE.md                ← MIT
  ├── SECURITY.md               ← inventaire fail-safes
  └── logs\                     ← dossier logs (créé à la 1ère exécution)
      

Étape 2 — Configurer

Ouvrir nova-agent-config.toml dans le bloc-notes et adapter :

[server]
endpoint = "https://nova-obsrouen.pages.dev"
heartbeat_interval_s = 30
token = "nva_xxxxxxxxxxxxxxxxxxxxxxxx"  # à coller depuis le mail d'activation

[observatory]
name = "Observatoire de Rouen"
lat = 49.44649
lon = 1.09760
altitude_m = 65
timezone = "Europe/Paris"

[hardware]
mount_driver = "ASCOM.Temma.Telescope"
camera_driver = "ASCOM.Atik460EX.Camera"
guider_driver = "ASCOM.ASIGuide.Camera"
focuser_driver = "ASCOM.Robofocus.Focuser"

[control]
enabled = false          # ← MODE LECTURE SEULE PAR DÉFAUT
max_slew_per_hour = 20
max_session_duration_min = 480
require_acknowledge = true
abort_on_clouds = true

[safety]
min_altitude_deg = 5
max_altitude_deg = 89
min_exposure_s = 0
max_exposure_s = 1800
kill_switch_path = "C:\\nova-agent\\STOP"
park_on_disconnect = true
park_on_dawn = true

[logging]
log_dir = "logs"
log_level = "info"
log_rotate_days = 30
      

Étape 3 — Premier lancement (mode lecture seule)

Double-cliquer nova-agent.exe. Une console s'ouvre :

nova-agent v0.1.0
Mode: READ-ONLY (control.enabled = false)
Heartbeat target: https://nova-obsrouen.pages.dev/api/control/heartbeat
Observatory: Observatoire de Rouen (49.44649, 1.09760)
ASCOM Telescope: ASCOM.Temma.Telescope ............ OK
ASCOM Camera:    ASCOM.Atik460EX.Camera ........... OK
ASCOM Guider:    ASCOM.ASIGuide.Camera ............ OK
ASCOM Focuser:   ASCOM.Robofocus.Focuser .......... OK
Heartbeat #1 sent (200 OK, 142 ms)
Kill switch: not present (OK)
Listening for commands... (READ-ONLY: commands logged but not executed)
      

L'agent envoie un heartbeat toutes les 30 s avec le statut hardware (température capteur, position monture, météo, connexions ASCOM). Si une commande arrive (depuis /control ou l'API), elle est loguée mais non exécutée.

Étape 4 — Activer le mode pilotage (optionnel)

⚠ Étape critique — autorisation explicite

L'activation du mode pilotage doit faire l'objet d'une autorisation écrite du responsable de l'observatoire (Eric Mandon pour Rouen). NOVA ne suppose aucun pilotage sans accord.

Pour activer :

  1. Arrêter l'agent (Ctrl+C ou fermer la console).
  2. Éditer nova-agent-config.toml[control] enabled = true.
  3. Relancer l'agent. Le banner affichera maintenant Mode: CONTROL (commands will be executed).
  4. Le kill switch reste actif : créer le fichier C:\nova-agent\STOP coupe l'agent en < 10 s.

Installation — Linux (avancé)

Pour les setups Linux (ce n'est pas le cas de Rouen, mais documenté pour les futures stations) :

# Téléchargement
$ wget https://nova-obsrouen.pages.dev/dl/nova-agent-linux-x64.tar.gz
$ tar xzf nova-agent-linux-x64.tar.gz -C /opt/

# Service systemd
$ sudo cp /opt/nova-agent/nova-agent.service /etc/systemd/system/
$ sudo systemctl daemon-reload
$ sudo systemctl enable --now nova-agent

# Logs
$ journalctl -u nova-agent -f
      

Sur Linux, l'agent utilise INDI au lieu d'ASCOM (driver indi-temma, indi-atik, etc.). Tous les fail-safes restent identiques.

Protocole heartbeat

Toutes les 30 s, l'agent envoie un POST :

POST /api/control/heartbeat HTTP/1.1
Host: nova-obsrouen.pages.dev
Authorization: Bearer nva_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

{
  "agent_version": "0.1.0",
  "observatory": "Observatoire de Rouen",
  "timestamp": "2026-06-21T22:34:15.123Z",
  "mode": "control",
  "hardware": {
    "mount": {
      "connected": true,
      "tracking": true,
      "ra_hours": 5.587,
      "dec_deg": 41.269,
      "alt_deg": 67.2,
      "az_deg": 102.5
    },
    "camera": {
      "connected": true,
      "temp_C": -15.2,
      "cooling_pct": 64
    },
    "guider": { "connected": true, "rms_arcsec": 0.82 },
    "focuser": { "connected": true, "position": 18432, "temperature_C": 11.8 }
  },
  "weather": {
    "source": "open-meteo",
    "cloud_pct": 12,
    "humidity_pct": 78,
    "wind_kmh": 8.5,
    "temperature_C": 12.3
  },
  "current_session": {
    "target": "M31",
    "filter": "L",
    "subs_completed": 14,
    "subs_planned": 30,
    "elapsed_min": 42
  },
  "alerts": []
}

Le serveur répond avec :

200 OK
{
  "ok": true,
  "serverTime": "2026-06-21T22:34:15.456Z",
  "next_heartbeat_s": 30
}

Queue de commandes

Après chaque heartbeat, l'agent fait un GET sur la queue :

GET /api/control/queue HTTP/1.1
Authorization: Bearer nva_xxxxxxxxxxxxxxxxxxxxxxxx

200 OK
[
  {
    "id": "cmd-2026-06-21-001",
    "issued_at": "2026-06-21T22:33:00Z",
    "cmd": "goto",
    "params": { "ra_hours": 0.7128, "dec_deg": 41.269 },
    "deadline": "2026-06-21T22:40:00Z",
    "ack_required": true
  }
]

L'agent valide chaque commande contre ses fail-safes hard-codés avant exécution :

  • Altitude calculée pour les coordonnées demandées doit être dans [min_altitude_deg, max_altitude_deg].
  • Exposure dans [min_exposure_s, max_exposure_s].
  • Token bearer valide et préfixé nva_.
  • control.enabled = true.
  • Fichier STOP absent.
  • Quota slew/h non dépassé.

Si une validation échoue, l'agent POST un résultat "status": "rejected" avec la raison, sans exécuter.

Résultats de commande

Après exécution ou rejet, l'agent POST :

POST /api/control/result HTTP/1.1
Authorization: Bearer nva_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

{
  "cmd_id": "cmd-2026-06-21-001",
  "status": "ok",
  "executed_at": "2026-06-21T22:35:12.123Z",
  "duration_ms": 8421,
  "result": {
    "platesolve": {
      "ra_hours": 0.7129,
      "dec_deg": 41.270,
      "rotation_deg": 178.3,
      "scale_arcsec_per_px": 0.334
    },
    "final_position_error_arcsec": 3.2
  },
  "logs": [
    "2026-06-21T22:35:03 SLEW start",
    "2026-06-21T22:35:08 SLEW done (5.1s)",
    "2026-06-21T22:35:09 CAPTURE 5s for solve",
    "2026-06-21T22:35:14 SOLVE start (ASTAP)",
    "2026-06-21T22:35:16 SOLVE done (RA 0.7129 Dec 41.270)",
    "2026-06-21T22:35:17 SYNC mount",
    "2026-06-21T22:35:18 GOTO offset 8.3'",
    "2026-06-21T22:35:23 IDLE position locked"
  ]
}

Inventaire des fail-safes

#Fail-safeDescriptionBypass possible ?
FS1Mode lecture seule par défautL'agent démarre toujours avec control.enabled = falseÉdition fichier config + restart
FS2Kill switch fichierPrésence de STOP dans le dossier agent ⇒ arrêt en < 10 sNon (hardcoded)
FS3Token bearer obligatoireRefus si Authorization manquant ou ne commence pas par nva_Non (hardcoded)
FS4Altitude minimaleRefus slew si Alt < 5° (collisions trépied)Non (hardcoded)
FS5Altitude maximaleRefus slew si Alt > 89° (zone interdite zénith)Non (hardcoded)
FS6Plage exposureRefus si exposure_s hors [0, 1800]Non (hardcoded)
FS7Timeout commandeToute commande abandon après 60 s sans réponse hardwareNon (hardcoded)
FS8Quota slew par heureMax 20 slew/h pour limiter usure mécaniqueConfig (par défaut 20)
FS9Quota durée sessionMax 480 min (8 h) par session continueConfig (par défaut 480)
FS10Park on disconnectSi le serveur ne répond plus pendant 5 heartbeats consécutifs ⇒ park mountConfig
FS11Park on dawnPark automatique 30 min avant lever du SoleilConfig
FS12Connexion sortante uniquementAucun port n'est ouvert côté observatoireNon (architecture)
FS13Logs locaux indépendantsTous les événements sont écrits dans logs\ en clair, auditablesNon (hardcoded)
FS14Mode dry-runFlag CLI --dry-run exécute toute la logique sans envoyer les commandes ASCOMFlag

Modèle de menace et de confiance

Trois acteurs sont en jeu :

  1. L'opérateur d'observatoire (Eric Mandon, Rouen) — confiance ultime, maître du matériel, peut tout arrêter.
  2. L'opérateur NOVA (Fabrice Langlois) — confiance technique, peut envoyer des commandes mais soumises aux fail-safes.
  3. L'utilisateur final — confiance limitée, ses commandes passent par le scheduler qui les valide avant de les mettre en queue.

Menaces couvertes :

  • Commande malveillante en queue ⇒ fail-safes refusent (altitude, exposure, quota).
  • Compromission du token ⇒ rotation manuelle par l'opérateur observatoire (regénérer config + restart).
  • Bug logiciel du serveur ⇒ kill-switch fichier reste accessible localement.
  • Coupure réseau ⇒ park on disconnect.
  • Conditions météo dégradées ⇒ abort_on_clouds vérifie chaque heartbeat.

Menaces non couvertes (à ce stade) :

  • Compromission du PC mini-PC MeLE (malware Windows) — au-delà du périmètre de l'agent.
  • Attaque physique sur le matériel — vandalisme (problème historique de Rouen).
  • Chute de température extrême non détectée par capteur ambient — hardcoder un seuil futur.

Mise à jour de l'agent

La mise à jour est manuelle par design — pas d'auto-update silencieux :

  1. L'opérateur reçoit un mail de notification avec checksum SHA-256 de la nouvelle version.
  2. Télécharger le nouveau ZIP depuis https://nova-obsrouen.pages.dev/dl/nova-agent-windows-x64-v0.2.zip.
  3. Vérifier le SHA-256 (commande PowerShell : Get-FileHash file.zip).
  4. Arrêter l'agent (Ctrl+C).
  5. Renommer l'ancien binaire en nova-agent-v0.1.exe.bak.
  6. Décompresser le nouveau, conserver le nova-agent-config.toml existant.
  7. Relancer.

Les notes de version sont publiées sur /dl/CHANGELOG.txt.

Désinstaller

  1. Arrêter l'agent (Ctrl+C).
  2. Supprimer le dossier C:\nova-agent\.
  3. Optionnel : supprimer la tâche planifiée si configurée (Task Scheduler → nova-agent).

L'agent ne modifie aucune entrée registre Windows. La suppression est complète.

Suite logique