Aller au contenu principal

V0.2.0 — API Site, auth et Caméras

Publiée le 01/10/2026.

Documentation fonctionnelle

Ce que change la version pour les collaborateurs

Pourquoi​

Après la 0.1.0, les Flux simulés existaient mais le Site ne connaissait aucune Caméra : rien pour en déclarer, savoir lesquelles répondent, ni protéger cet accès. Il manquait aussi une API sur laquelle brancher l'UI de la 0.3.0.

Ce qu'on peut faire maintenant​

  • Lancer l'API du Site (FastAPI + PostgreSQL) avec le même docker compose up -d --wait ; le schéma s'applique tout seul au démarrage, la base n'est pas exposée.
  • Se connecter en tant qu'Administrateur (compte local défini dans .env), consulter /api/moi, se déconnecter. Les sessions survivent à un redémarrage, expirent après 24 h, tombent si le mot de passe change ; 5 échecs en 15 min bloquent la connexion (429).
  • Créer, lire, modifier, désactiver puis supprimer des Caméras (nom, URL RTSP, emplacement), avec le mot de passe RTSP masqué dans toutes les réponses.
  • Voir l'état de chaque Caméra active (unknown / online / offline), déterminé par une sonde RTSP de fond toutes les 10 s.
  • Suivre le protocole de lancement pas à pas du README ; les limites de sécurité sont décrites dans docs/securite.md.

Documentation technique

Spec versionnée : docs/specs/v0.2.0-api-site-auth-cameras.md

Issue parente : #6

V0.2.0 — API Site, auth et Caméras

Problem Statement​

Le Site ne connaît encore aucune Caméra : les Flux simulés de la 0.1.0 existent, mais rien ne permet de déclarer des Caméras, de savoir lesquelles répondent, ni de protéger cet accès. L'Administrateur n'a pas d'API sur laquelle brancher l'UI de la 0.3.0, et rien ne lui dit qu'une Caméra a décroché.

Solution​

Une API du Site (FastAPI + PostgreSQL), lancée par le même docker compose, protégée par le compte local de l'Administrateur défini dans .env. L'Administrateur se connecte, crée, lit, modifie, désactive puis supprime des Caméras (nom, URL RTSP, emplacement). L'API sonde en tâche de fond chaque Caméra active et expose son état unknown / online / offline. Les choix de sécurité et leurs limites sont écrits dans un document dédié pour ne pas être oubliés avant de brancher de vraies caméras.

User Stories​

Stack​

  1. En tant que développeur, je veux que docker compose up -d --wait lance aussi l'API et sa base, pour tout démarrer d'une commande.
  2. En tant que développeur, je veux que l'image PostgreSQL soit épinglée, pour que la stack ne change pas toute seule.
  3. En tant que développeur, je veux que le schéma de base soit appliqué automatiquement au démarrage de l'API (migrations), pour qu'un clone neuf fonctionne sans étape manuelle.
  4. En tant que développeur, je veux que les données survivent à un docker compose down / up, pour ne pas ressaisir mes Caméras.
  5. En tant qu'Administrateur, je veux que PostgreSQL ne soit pas joignable depuis le réseau local, pour qu'une machine du réseau (ou une caméra compromise) ne puisse pas lire la base.
  6. En tant que développeur, je veux la documentation OpenAPI sur /api/docs, pour explorer l'API sans lire le code.

Administrateur et connexion​

  1. En tant qu'Administrateur, je veux définir mon identifiant et mon mot de passe dans .env (ARGOS_IDENTIFIANT, ARGOS_MOT_DE_PASSE), pour qu'aucun secret ne soit dans git.
  2. En tant qu'Administrateur, je veux que l'API refuse de démarrer si l'une de ces variables manque, avec un message clair, pour qu'il n'existe jamais de compte par défaut.
  3. En tant qu'Administrateur, je veux me connecter avec mon identifiant et mon mot de passe et recevoir un cookie de session HttpOnly, pour accéder à l'API.
  4. En tant qu'Administrateur, je veux qu'un mauvais identifiant ou mot de passe renvoie 401 sans dire lequel est faux, pour ne pas aider un attaquant.
  5. En tant qu'Administrateur, je veux consulter qui est connecté (GET /api/moi), pour que l'UI sache si la session est valide.
  6. En tant qu'Administrateur, je veux me déconnecter, pour que la session soit révoquée côté serveur.
  7. En tant qu'Administrateur, je veux qu'une session expire 24 h après la connexion, sans prolongation, pour limiter la durée d'un cookie volé.
  8. En tant qu'Administrateur, je veux que toutes les sessions ouvertes soient invalidées quand je change le mot de passe dans .env puis redémarre, pour couper un accès compromis.
  9. En tant qu'Administrateur, je veux que toute route des Caméras renvoie 401 sans session valide, pour que rien ne fuite sans connexion.
  10. En tant qu'Administrateur, je veux qu'après 5 échecs de connexion en 15 min la connexion réponde 429 pendant 15 min, pour freiner la force brute depuis le réseau local.
  11. En tant qu'Administrateur, je veux que les sessions survivent à un redémarrage de l'API (hors changement de mot de passe), pour ne pas être déconnecté à chaque mise à jour.

Caméras​

  1. En tant qu'Administrateur, je veux créer une Caméra avec un nom, une URL RTSP et un emplacement facultatif, pour que le Site la connaisse.
  2. En tant qu'Administrateur, je veux que le nom soit obligatoire et unique, pour distinguer les Caméras.
  3. En tant qu'Administrateur, je veux que l'URL soit obligatoire, unique et en rtsp://, pour ne pas déclarer deux fois la même Caméra ni une URL invalide.
  4. En tant qu'Administrateur, je veux un 409 en cas de doublon de nom ou d'URL, et un 422 pour une URL invalide, pour comprendre le refus.
  5. En tant qu'Administrateur, je veux lister toutes les Caméras (actives et désactivées), pour avoir la vue d'ensemble du Site.
  6. En tant qu'Administrateur, je veux lire une Caméra, pour voir ses informations et son état.
  7. En tant qu'Administrateur, je veux voir l'hôte et le port dérivés de l'URL, pour connaître ses infos réseau sans les ressaisir.
  8. En tant qu'Administrateur, je veux pouvoir écrire les identifiants RTSP dans l'URL (rtsp://user:motdepasse@ip/...), parce que la plupart des vraies caméras l'exigent.
  9. En tant qu'Administrateur, je veux que l'API masque le mot de passe RTSP dans toutes ses réponses (rtsp://user:***@ip/...), pour qu'il ne s'affiche pas à l'écran ni dans les logs du navigateur.
  10. En tant qu'Administrateur, je veux que renvoyer l'URL masquée telle quelle lors d'une modification conserve le mot de passe stocké, pour modifier le nom ou l'emplacement sans ressaisir le mot de passe.
  11. En tant qu'Administrateur, je veux modifier le nom, l'URL ou l'emplacement d'une Caméra, pour corriger une erreur.
  12. En tant qu'Administrateur, je veux qu'un changement d'URL remette l'état à unknown, pour ne pas afficher l'état de l'ancienne adresse.
  13. En tant qu'Administrateur, je veux désactiver une Caméra, pour que le Site la connaisse encore mais ne la surveille plus.
  14. En tant qu'Administrateur, je veux réactiver une Caméra désactivée, pour reprendre sa surveillance.
  15. En tant qu'Administrateur, je veux qu'une Caméra désactivée garde son nom et son URL réservés, pour éviter un doublon à sa réactivation.
  16. En tant qu'Administrateur, je veux supprimer définitivement une Caméra désactivée (204), pour nettoyer le Site.
  17. En tant qu'Administrateur, je veux que la suppression d'une Caméra active soit refusée (409), pour éviter une suppression accidentelle.
  18. En tant qu'Administrateur, je veux un 404 sur une Caméra inexistante, pour distinguer « absente » de « refusée ».

État​

  1. En tant qu'Administrateur, je veux voir l'état unknown / online / offline de chaque Caméra, pour savoir d'un coup d'œil laquelle a décroché.
  2. En tant qu'Administrateur, je veux voir quand l'état a été vérifié pour la dernière fois (etat_verifie_le), pour savoir si l'information est fraîche.
  3. En tant qu'Administrateur, je veux qu'une Caméra soit online seulement si au moins un paquet vidéo a été reçu d'elle, parce que « on reçoit du flux » est la définition d'une Caméra active.
  4. En tant qu'Administrateur, je veux qu'une Caméra qui ne livre aucun paquet vidéo en 5 s passe offline dès la première sonde ratée, pour être prévenu sans délai.
  5. En tant qu'Administrateur, je veux que chaque Caméra active soit sondée toutes les 10 s, pour que l'état suive la réalité.
  6. En tant qu'Administrateur, je veux régler l'intervalle et le délai de sonde dans .env, pour les adapter à mon réseau.
  7. En tant qu'Administrateur, je veux qu'une Caméra nouvelle soit unknown tant qu'elle n'a pas été sondée, pour ne pas afficher un état inventé.
  8. En tant qu'Administrateur, je veux qu'une Caméra désactivée ne soit plus sondée et soit unknown, pour ne pas consommer une connexion sur une caméra qu'on ne surveille plus.
  9. En tant que développeur, je veux qu'une Caméra simulée déclarée avec rtsp://mediamtx:8554/cam1 passe online, pour vérifier la sonde sans matériel.

Documentation​

  1. En tant que développeur, je veux que le glossaire définisse Site, Administrateur et Caméra désactivée, pour parler le même langage.
  2. En tant que développeur, je veux un document de sécurité (docs/securite.md, rôle security) qui explique ce que prouve l'état d'une Caméra (et le scénario de rejeu de flux), l'exposition réseau dans une installation type et les dettes connues, pour ne pas oublier ces limites avant de brancher de vraies caméras.
  3. En tant que développeur, je veux que la feuille de route dise que les identifiants RTSP sont stockés en clair pour l'instant, et liste en « Plus tard » : chiffrement des identifiants RTSP, HTTPS, authentification des Flux MediaMTX, accès distant par VPN, détection de Flux figé ou rejoué.
  4. En tant que développeur, je veux une contrainte transverse dans la feuille de route : pas d'installation sur de vraies caméras avant HTTPS et authentification des Flux.
  5. En tant que nouveau venu, je veux que le README explique .env (identifiants), comment lancer l'API, comment ajouter les trois Caméras simulées avec curl (URL rtsp://mediamtx:8554/camN et pourquoi), comment tester, et la nouvelle structure.

Implementation Decisions​

  • Site : une installation d'ArgOS. Un seul Site par installation ; ce n'est pas une ressource de l'API.
  • Stack : deux services rejoignent la stack Compose existante : api (Python ≥ 3.12, FastAPI, uv) et db (PostgreSQL, image épinglée, aucun port publié, volume nommé). Migrations Alembic appliquées au démarrage de api. api publie le port 8000 sur toutes les interfaces. MediaMTX inchangé.
  • Administrateur : compte unique, identifiant et mot de passe lus dans l'environnement (.env). Absence de l'une des variables → l'API ne démarre pas. Comparaison du mot de passe à temps constant ; rien n'est haché ni stocké, puisque la source est .env. .env.example documente les clés et recommande un mot de passe long.
  • Sessions : jeton aléatoire opaque dans un cookie HttpOnly, SameSite=Lax (pas Secure tant qu'il n'y a pas de HTTPS). Session stockée en base, durée absolue de 24 h, sans prolongation. Chaque session garde une empreinte du mot de passe en vigueur à sa création : si le mot de passe de .env change, toutes les sessions existantes sont refusées. Déconnexion = suppression de la session.
  • Frein à la force brute : compteur global en mémoire (un seul compte). 5 échecs dans une fenêtre de 15 min → 429 sur la connexion pendant 15 min. Remis à zéro au redémarrage.
  • Horloge et configuration injectables dans l'application, pour que les règles temporelles (expiration, fenêtre du 429) et de configuration (changement de mot de passe) soient testables sans attendre.
  • Contrat HTTP (JSON, erreurs FastAPI standard \{"detail": ...\}) :
    • POST /api/session \{identifiant, mot_de_passe\} → 204 + cookie ; 401 ; 429.
    • DELETE /api/session → 204.
    • GET /api/moi → \{identifiant\} ; 401.
    • GET /api/cameras → liste ; POST /api/cameras \{nom, url_rtsp, emplacement?\} → 201.
    • GET /api/cameras/\{id\} ; PATCH /api/cameras/\{id\} (champs partiels, dont active) ; DELETE /api/cameras/\{id\} → 204, 409 si active.
    • Représentation d'une Caméra : id, nom, url_rtsp (mot de passe masqué), emplacement, hote, port, active, etat, etat_verifie_le.
  • Règles Caméra : nom et URL uniques (y compris parmi les désactivées) → 409. URL en rtsp:// avec hôte → sinon 422. Port par défaut 554 si absent de l'URL. URL renvoyée masquée inchangée en PATCH → mot de passe stocké conservé. Changement d'URL → etat = unknown, etat_verifie_le = null. Désactivation → etat = unknown, plus de sonde. Suppression définitive.
  • Identifiants RTSP stockés en clair dans la base pour la 0.2.0 ; masqués dans toutes les réponses. Dette consignée (feuille de route + document de sécurité).
  • Sonde d'état : boucle de fond dans le processus api, toutes les 10 s (.env), sondes en parallèle. Une sonde = session RTSP DESCRIBE / SETUP / PLAY (identifiants de l'URL pris en charge), réception d'au moins un paquet RTP vidéo dans le délai (5 s, .env), puis TEARDOWN. Succès → online, sinon → offline, horodaté. Pas d'hystérésis. Pas de ffmpeg dans l'image api. Un module de sonde à interface étroite (une URL et un délai en entrée, vrai/faux en sortie).
  • MediaMTX : pas de pilotage par l'API en 0.2.0. Le choix MediaMTX pont / simulateur est reporté à la 0.3.0. Depuis le conteneur api, les Caméras simulées se déclarent avec rtsp://mediamtx:8554/camN.
  • Base vide au démarrage : aucune Caméra pré-enregistrée.
  • Glossaire (CONTEXT.md) :
  • Document de sécurité docs/securite.md, nouveau rôle security dans agents/roles.yml : (1) ce que prouve l'état d'une Caméra : un paquet vidéo reçu, pas l'authenticité ; scénario de rejeu / injection de flux et défenses (VLAN, auth RTSP / RTSPS, détection d'image figée ou en boucle, événements ONVIF de sabotage) ; (2) exposition réseau dans l'installation type (serveur sur site, caméras, Wi-Fi, accès internet futur) : pourquoi PostgreSQL n'est pas exposé, Docker qui contourne le pare-feu de l'hôte, caméras comme maillon faible, Flux MediaMTX sans auth, API en HTTP, VPN plutôt que redirection de port / UPnP ; (3) dettes connues : identifiants RTSP en clair, pas de HTTPS, Flux sans auth. Le README y renvoie.
  • Feuille de route : section 0.2.0 complétée (identifiants RTSP en clair, masqués en sortie) ; contrainte transverse « pas d'installation sur de vraies caméras avant HTTPS et authentification des Flux » ; entrées « Plus tard » : chiffrement des identifiants RTSP, HTTPS, authentification des Flux MediaMTX, accès distant par VPN, détection de Flux figé ou rejoué.
  • Rôle test-roots : ajout des deux nouvelles racines de tests (HTTP et en processus).
  • Pas d'ADR : aucune décision ne remplit à la fois « difficile à défaire », « surprenante » et « vrai compromis ».

Testing Decisions​

  • Seam principal : HTTP contre la stack lancée, comme les tests de Flux de la 0.1.0 (prior art : la racine de tests des Flux, fixture de session qui lance docker compose up -d --wait). Couvre : refus sans session (401), connexion / moi / déconnexion, mauvais identifiants, CRUD complet, 409 doublons, 422 URL invalide, masquage du mot de passe et conservation en PATCH, hôte / port dérivés, désactivation / réactivation, suppression refusée si active, 404, unknown à la création, online pour rtsp://mediamtx:8554/cam1, offline pour une URL injoignable, 429 après 5 échecs.
  • Second seam, étroit : l'application en processus (FastAPI monté en mémoire, horloge et configuration injectées, base PostgreSQL de test). Uniquement pour les règles impossibles à tester vite en HTTP : expiration à 24 h, fin du 429 après 15 min, invalidation des sessions au changement de mot de passe, refus de démarrer sans identifiants.
  • Les tests HTTP n'effacent jamais la base : chaque test crée ses Caméras avec des noms uniques puis les désactive et les supprime. Le test du 429 remet l'API dans un état propre (redémarrage de api ou ordre d'exécution, à trancher au ticket).
  • Identifiants des tests lus dans .env ; s'ils manquent, échec immédiat avec un message qui dit quoi ajouter. Pas de compte de test caché.
  • Un bon test vérifie un comportement observable (réponse HTTP, état d'une Caméra), pas la forme des tables ni la commande RTSP envoyée.
  • /t lance toutes les racines de test-roots.

Out of Scope​

  • UI (0.3.0), pilotage de MediaMTX par l'API, choix MediaMTX pont / simulateur, WebRTC vs HLS.
  • Plusieurs comptes, rôles, gestion des comptes par l'API, Administrateur système.
  • HTTPS, cookie Secure, chiffrement des identifiants RTSP, authentification des Flux MediaMTX, accès distant / VPN.
  • Hystérésis de l'état, historique des états, événements, alertes, détection de Flux figé ou rejoué.
  • Archivage des Caméras supprimées.
  • Enregistrement, IA, ONVIF, PTZ, codecs autres que H.264.
  • CI GitHub Actions ; analyse SonarCloud par Automatic Analysis (comme BBASS), pas de scanner CI.

Further Notes​

  • online signifie « un paquet vidéo a été reçu », pas « c'est la vraie caméra » : voir le document de sécurité.
  • Sur Mac / Docker Desktop, la sonde vers MediaMTX passe par le réseau Compose interne (mediamtx), pas par localhost.
  • Contexte : feuille de route de dev (section 0.2.0), spec 0.1.0, contexte initial.