{"components":{"schemas":{"AbonnementSseReq":{"description":"Corps d'un abonnement SSE (miroir ToSchema).","properties":{"genre":{"description":"Préfixe de genre filtré (ex. « vm. ») — vide = tout.","type":["string","null"]},"id":{"description":"Id libre du côté client (1-64 caractères) — le curseur au reconnexion.","type":"string"}},"required":["id"],"type":"object"},"AbonnementStatusReq":{"description":"La demande d'abonnement à la status page.","properties":{"destination":{"description":"URL http(s)://… de notification (webhook) ou de flux (rss).","type":"string"},"genre":{"description":"webhook | rss.","type":"string"}},"required":["genre","destination"],"type":"object"},"AccepterSuggestionReq":{"properties":{"nom":{"description":"Nom déterministe de la suggestion (ex. waf-appris-scan).","type":"string"}},"required":["nom"],"type":"object"},"AckEchoueEdge":{"description":"Un échec ACK MESURÉ qui motive le rollback — jamais une intuition.","properties":{"composant":{"type":"string"},"detail":{"type":["string","null"]},"version":{"description":"Version dont l'application a échoué.","format":"int32","minimum":0,"type":"integer"}},"required":["composant","version"],"type":"object"},"ActionEdgeReq":{"properties":{"charge":{},"cle":{"type":"string"},"genre":{"description":"maj_shadow | publication | commande | evenement.","type":"string"},"locale_le":{"description":"Horodatage local du device (RFC 3339).","type":"string"}},"required":["cle","genre","charge","locale_le"],"type":"object"},"AddonReq":{"description":"Corps de pose d'un add-on sur un cluster.","properties":{"code":{"description":"Code du catalogue (ingress | cert-manager | monitoring).","type":"string"}},"required":["code"],"type":"object"},"Adhesion":{"properties":{"org_name":{"type":"string"},"org_slug":{"type":"string"},"role":{"type":"string"}},"required":["org_slug","org_name","role"],"type":"object"},"AdmettreFileReq":{"properties":{"max_concurrentes":{"description":"Requêtes actives maximum de l'entrepôt — la borne D'ADMISSION,\nexplicite, jamais devinée.","minimum":0,"type":"integer"},"priorite":{"description":"1 (haute) à 5 (basse).","format":"int32","minimum":0,"type":"integer"}},"required":["priorite","max_concurrentes"],"type":"object"},"AdmettreGpuReq":{"description":"Corps d'admission d'un workspace sur un GPU.","properties":{"fractions":{"description":"Fractions demandées (1-8).","format":"int32","minimum":0,"type":"integer"},"workspace":{"description":"Workspace à admettre (1-60 caractères, un par GPU).","type":"string"}},"required":["workspace","fractions"],"type":"object"},"AdmissionReq":{"properties":{"en_cours":{"description":"Requêtes en vol MESURÉES par l'appelant (la plateforme n'a pas\nde compteur en vol applicatif — on ne le déduit pas).","format":"int64","minimum":0,"type":"integer"},"requetes":{"description":"Requêtes en vol (identifiant, début epoch s) pour compter les\nexpirées au timeout — facultatif.","items":{"$ref":"#/components/schemas/RequeteEnVolReq"},"type":"array"}},"required":["en_cours"],"type":"object"},"AffecterIpReq":{"properties":{"cible":{"description":"cible : vmid numérique (machine Proxmox) ou nom libre (NAT…)","type":"string"},"cible_type":{"type":"string"},"ip":{"description":"IP précise dans la plage, ou vide = allocation automatique","type":["string","null"]},"plage_id":{"type":"string"}},"required":["plage_id","cible","cible_type"],"type":"object"},"AffinerReq":{"properties":{"base_version":{"description":"Version du registre servant de base (cadre gguf, artefact local).","format":"int32","minimum":0,"type":"integer"},"epochs":{"description":"Passes d'entraînement (1-20, défaut 2).","format":"int32","minimum":0,"type":["integer","null"]},"exemples":{"description":"Paires instruction/réponse (4 à 200).","items":{"$ref":"#/components/schemas/Exemple"},"type":"array"},"lr":{"description":"Taux d'apprentissage (1e-6 à 1e-2, défaut 1e-4).","format":"double","type":["number","null"]},"note":{"description":"Note libre portée par le travail.","type":"string"}},"required":["base_version","exemples"],"type":"object"},"AffiniteReq":{"properties":{"ram_mb":{"format":"int32","minimum":0,"type":"integer"},"vcpu":{"format":"int32","minimum":0,"type":"integer"},"vm":{"description":"VM existante à épingler (exigé à l'intégration).","type":"string"}},"required":["vm","vcpu","ram_mb"],"type":"object"},"Agent":{"properties":{"cree_le":{"type":"string"},"description":{"type":"string"},"id":{"type":"string"},"instructions":{"description":"Prompt système métier de l'agent.","type":"string"},"llm_id":{"description":"Endpoint LLM de l'org (actif exigé pour les runs).","type":"string"},"memoire":{"description":"Mémoire persistante activée.","type":"boolean"},"nom":{"type":"string"},"org":{"type":"string"},"outils":{"description":"Outils autorisés — sous-ensemble du catalogue.","items":{"type":"string"},"type":"array"},"politique":{"$ref":"#/components/schemas/Politique"}},"required":["id","org","nom","description","instructions","llm_id","outils","memoire","politique","cree_le"],"type":"object"},"Agregation":{"description":"L'agrégation calculée sur la fenêtre glissante.","enum":["Dernier","Somme","Moyenne","Compteur","Minimum","Maximum"],"type":"string"},"AgregerNfsReq":{"properties":{"membres":{"description":"Ids nfs-… des partages membres, DANS L'ORDRE : le PREMIER est la\ncible d'écriture, tous servent les lectures.","items":{"type":"string"},"type":"array"},"point_montage":{"description":"Point de montage UNIQUE de l'agrégat (ABSOLU — ex. /mnt/agrege-donnees).","type":"string"}},"required":["point_montage","membres"],"type":"object"},"AliasBurstApiReq":{"description":"Miroir API d'un alias de burst (noyau sans ToSchema).","properties":{"modele":{"description":"Modèle servi par la cible.","type":"string"},"mots_interdits":{"description":"Politique de fuite : aucun de ces mots ne quitte le lab.","items":{"type":"string"},"type":"array"},"nom":{"description":"Nom de l'alias (1-60 caractères).","type":"string"},"url":{"description":"URL de la cible externe (http:// ou https://).","type":"string"}},"required":["nom","url","modele","mots_interdits"],"type":"object"},"AnalyserLotReq":{"properties":{"page":{"description":"Page demandée (1-based, défaut 1).","minimum":0,"type":"integer"},"taille_page":{"description":"Taille de page (défaut 10).","minimum":0,"type":"integer"},"textes":{"description":"Textes à analyser (≤ 25 par lot — la limite est explicite).","items":{"type":"string"},"type":"array"}},"required":["textes"],"type":"object"},"AnalyserReq":{"properties":{"source":{"description":"Chemin absolu du dossier à balayer (dépôt hors-ligne, export…).","type":"string"}},"required":["source"],"type":"object"},"AnalyserTexteReq":{"properties":{"texte":{"type":"string"}},"required":["texte"],"type":"object"},"ApiKey":{"properties":{"created_at":{"type":"string"},"expires_at":{"type":["string","null"]},"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"revoked_at":{"type":["string","null"]}},"required":["id","prefix","name","created_at"],"type":"object"},"App":{"properties":{"conge_auto":{"description":"Scale-to-zero : `true` ⇒ la app se met en veille sans trafic.","type":"boolean"},"created_at":{"type":"string"},"deleted_at":{"type":["string","null"]},"dernier_acces":{"description":"Dernier accès (secondes epoch) — 0 si jamais consultée.","format":"int64","minimum":0,"type":"integer"},"etat":{"description":"provisioning | running | parked","type":"string"},"id":{"type":"string"},"image":{"description":"Image conteneur (ex. `ghcr.io/exemple/api:1.2`).","type":"string"},"max_instances":{"format":"int32","minimum":0,"type":"integer"},"min_instances":{"format":"int32","minimum":0,"type":"integer"},"nom":{"type":"string"},"port":{"description":"Port interne du conteneur.","format":"int32","minimum":0,"type":"integer"},"project_id":{"type":"string"},"revision_active":{"description":"Révision active (index dans revisions, 0 par défaut).","minimum":0,"type":"integer"},"revisions":{"description":"Révisions (style Knative) : image + part de trafic.","items":{"$ref":"#/components/schemas/Revision"},"type":"array"},"tunnel_token":{"description":"Jeton de tunnel (workspaces uniquement, vide sinon).","type":"string"},"url":{"type":"string"},"vm_id":{"description":"VM sous-jacente (workspaces uniquement, vide sinon).","type":"string"}},"required":["id","project_id","nom","image","port","min_instances","max_instances","conge_auto","etat","dernier_acces","url","tunnel_token","vm_id","revisions","revision_active","created_at"],"type":"object"},"AppComp":{"description":"Le composant app : une image conteneurisée ÉTIQUETÉE et son port\nd'écoute interne — déployée par le service apps.","properties":{"image":{"description":"Image étiquetée (« ghcr.io/lab/web:1.4.0 » — « :latest » refusé).","type":"string"},"port":{"description":"Port d'écoute interne, 1-65535.","format":"int32","minimum":0,"type":"integer"}},"required":["image","port"],"type":"object"},"ArbitrerBurstReq":{"description":"Corps d'un arbitrage de burst.","properties":{"en_public":{"description":"État mesuré : bascule-t-on déjà en public ?","type":"boolean"},"file_locale":{"description":"Longueur de la file locale MESURÉE (requêtes en attente).","minimum":0,"type":"integer"},"seuil_bascule":{"description":"Seuil de bascule vers le public (≥ 1).","minimum":0,"type":"integer"},"seuil_retour":{"description":"Seuil de retour au local — STRICTEMENT plus bas (hystérésis).","minimum":0,"type":"integer"},"texte":{"description":"Texte de la requête à arbitrer (la politique de fuite s'y applique).","type":"string"}},"required":["en_public","file_locale","seuil_bascule","seuil_retour","texte"],"type":"object"},"ArtefactSignatureReq":{"description":"Un artefact à faire figurer au manifeste de signature.","properties":{"empreinte":{"description":"Empreinte sha256 MESURÉE par l'appelant (64 hex minuscules).","type":"string"},"nom":{"description":"Nom de l'artefact (poids, tokenizer, config…).","type":"string"}},"required":["nom","empreinte"],"type":"object"},"AttacherDatasetReq":{"properties":{"modele":{"description":"Modèle (mdl-…).","type":"string"},"ref_dataset":{"description":"Référence du dataset « dts-…@N ».","type":"string"},"version_modele":{"format":"int32","minimum":0,"type":"integer"}},"required":["modele","version_modele","ref_dataset"],"type":"object"},"AttacherEvalReq":{"description":"Corps d'attachement d'une éval (exécution d'une suite cirroAgents).","properties":{"execution":{"description":"Id de l'EXÉCUTION qui a produit le score (exe-…).","type":"string"},"suite":{"description":"Nom de la suite d'éval (celui de cirroAgents).","type":"string"}},"required":["suite","execution"],"type":"object"},"AttacherReq":{"properties":{"machine":{"description":"Nom (ou id) de la machine à attacher au réseau.","type":"string"}},"required":["machine"],"type":"object"},"AttribuerReq":{"properties":{"org":{"type":"string"}},"required":["org"],"type":"object"},"BackupInstance":{"properties":{"chemin":{"type":"string"},"ts":{"type":"string"}},"required":["ts","chemin"],"type":"object"},"BailReq":{"properties":{"client":{"description":"VM ou client qui demande un bail.","type":"string"},"duree_s":{"description":"Durée demandée (s) — plafonnée à DUREE_BAIL_S.","format":"int64","minimum":0,"type":["integer","null"]}},"required":["client"],"type":"object"},"BalayerColonnesReq":{"properties":{"base":{"type":"string"},"colonnes":{"items":{"$ref":"#/components/schemas/ColonneMeta"},"type":"array"}},"required":["base","colonnes"],"type":"object"},"BasculeCacheReq":{"properties":{"primaire_repond":{"description":"Le primaire répond-il ? MESURÉ par l'intégration (PING).","type":"boolean"}},"required":["primaire_repond"],"type":"object"},"BasculeLectureReq":{"description":"Corps d'une bascule de lecture.","properties":{"depuis_id":{"description":"Réplique à retirer du pool de lecture.","type":"string"},"motif":{"description":"Motif de la bascule — celui de l'APPELANT (la panne se mesure ailleurs).","type":"string"},"retard_max_ms":{"description":"Retard maximum exigé après bascule (ms).","format":"int64","minimum":0,"type":"integer"}},"required":["depuis_id","motif","retard_max_ms"],"type":"object"},"BasculeShardReq":{"description":"Corps de bascule d'un shard sur un nœud désigné.","properties":{"noeud":{"type":"string"}},"required":["noeud"],"type":"object"},"BaseComp":{"description":"Le composant base : un moteur parmi [`MOTEURS`].","properties":{"moteur":{"description":"ferrodb | postgres","type":"string"}},"required":["moteur"],"type":"object"},"BlobChiffre":{"description":"Blob chiffré par passphrase — { algo, sel, nonce, donnees_hex }.\nAES-256-GCM, clé dérivée de la passphrase par Argon2id sur `sel`.","properties":{"algo":{"description":"Étiquette de l'algorithme (ex. `aes-256-gcm+argon2id`).","type":"string"},"donnees_hex":{"description":"Chiffré + tag AES-GCM (hex).","type":"string"},"nonce":{"description":"Nonce AES-GCM 96 bits (hex).","type":"string"},"sel":{"description":"Sel de dérivation Argon2id (hex).","type":"string"}},"required":["algo","sel","nonce","donnees_hex"],"type":"object"},"BornesGpuReq":{"description":"Corps API des bornes d'un pool GPU (miroir ToSchema de\n[`crate::llm::BornesGpu`]).","properties":{"endpoints_max":{"minimum":0,"type":"integer"},"endpoints_min":{"description":"Endpoints actifs minimum (≥ 1 : un pool vide ne sert rien).","minimum":0,"type":"integer"},"inactif_arret_min":{"description":"Minutes d'inactivité PROUVÉE qui déclenchent un arrêt.","format":"int64","minimum":0,"type":"integer"},"seuil_demarrage":{"description":"Longueur d'attente qui déclenche un démarrage (≥ 1 requête).","minimum":0,"type":"integer"}},"required":["endpoints_min","endpoints_max","seuil_demarrage","inactif_arret_min"],"type":"object"},"BucketCree":{"description":"POST /v1/projects/{pid}/buckets — bucket créé sur la passerelle S3\nréelle ET au registre.","properties":{"created_at":{"type":"string"},"endpoint":{"description":"Passerelle S3 du cluster plateforme (la passerelle sans auth\naccepte tout credential : les clés IAM S3 restent la bonne voie).","type":"string"},"equivalents":{"description":"Équivalents CLI/OpenTofu de la création."},"id":{"type":"string"},"nom":{"type":"string"},"taille_limite_gb":{"format":"int64","type":"integer"},"usage":{"description":"Mode d'emploi aws cli.","type":"string"}},"required":["id","nom","taille_limite_gb","created_at","endpoint","usage","equivalents"],"type":"object"},"BucketNonEnregistre":{"description":"Bucket présent côté passerelle mais absent du registre.","properties":{"nom":{"type":"string"},"note":{"description":"Conduite à tenir (adopter via POST /buckets ou supprimer).","type":"string"}},"required":["nom","note"],"type":"object"},"BucketVue":{"description":"Un bucket servi en liste.","properties":{"compte_tronque":{"description":"Présent en source s3 uniquement : liste tronquée par le budget\ntemps de la passerelle.","type":["boolean","null"]},"created_at":{"type":"string"},"deleted_at":{"type":["string","null"]},"id":{"type":"string"},"nb_objets":{"description":"Compteur du registre, ou comptage live de la passerelle.","format":"int64","type":"integer"},"nb_octets":{"description":"Présent en source s3 uniquement : taille exacte en octets.","format":"int64","type":["integer","null"]},"nom":{"type":"string"},"project_id":{"type":"string"},"source":{"description":"s3 = compteurs en direct de la passerelle ; registre = repli.","type":"string"},"taille_limite_gb":{"format":"int64","type":"integer"},"taille_utilisee_mo":{"description":"Mo (registre), récalculé au centime en live.","format":"double","type":"number"}},"required":["id","project_id","nom","taille_limite_gb","nb_objets","taille_utilisee_mo","created_at","source"],"type":"object"},"BucketsListe":{"description":"GET /v1/projects/{pid}/buckets — buckets du registre, compteurs en\ndirect quand la passerelle répond (repli honnête « registre »).","properties":{"buckets_non_enregistres":{"description":"Présent seulement quand la passerelle répond : buckets créés\nHORS produit (PUT direct) — signalés, jamais adoptés en silence.","items":{"$ref":"#/components/schemas/BucketNonEnregistre"},"type":["array","null"]},"buckets_non_enregistres_total":{"minimum":0,"type":["integer","null"]},"items":{"items":{"$ref":"#/components/schemas/BucketVue"},"type":"array"},"total":{"minimum":0,"type":"integer"}},"required":["items","total"],"type":"object"},"BudgetPorteeReq":{"description":"La demande de création d'un budget à périmètre.","properties":{"limite_mois":{"format":"double","type":"number"},"nom":{"type":"string"},"portee":{"description":"`{\"portee\":\"org\"}` ou `{\"portee\":{\"tag\":{\"cle\":\"projet\",\"valeur\":\"facture\"}}}`\n(représentation serde de l'énum `Portee` du module budgets)."}},"required":["nom","portee","limite_mois"],"type":"object"},"BudgetUsageVue":{"description":"Budget/usage machines (B6 : RAM et disque en Go ; les quantités en\nMo restent servies sous ram_mb ailleurs dans l'API).","properties":{"disque_gb":{"format":"int64","type":"integer"},"ram_gb":{"format":"int64","type":"integer"},"vcpus":{"format":"int64","type":"integer"}},"required":["vcpus","ram_gb","disque_gb"],"type":"object"},"BundleConfiance":{"description":"Bundle de confiance exposé aux workloads : la clé publique CA + sa date.","properties":{"algorithme":{"type":"string"},"ca_pub_b64":{"type":"string"},"emis_a":{"format":"int64","minimum":0,"type":"integer"}},"required":["ca_pub_b64","emis_a","algorithme"],"type":"object"},"BundleExport":{"properties":{"contenu":{"description":"Familles déclaratives (`contenu.declaratif.*`) et références\n(`contenu.references.*`)."},"donnees":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/DonneesExport","description":"Section données — présente uniquement en mode\n`configuration_donnees`."}]},"empreinte":{"description":"sha256 du contenu canonisé (v2 : mode + contenu + donnees) —\nvérifiée à l'import.","type":"string"},"format":{"type":"string"},"genere_le":{"type":"string"},"genere_par":{"description":"Version cirrostratus ayant généré le bundle.","type":"string"},"mode":{"description":"`configuration` (défaut, compatible v1) ou `configuration_donnees`.","type":"string"},"org":{"$ref":"#/components/schemas/BundleOrg"},"resume":{"$ref":"#/components/schemas/ResumeExport"},"version_format":{"format":"int32","minimum":0,"type":"integer"}},"required":["format","version_format","genere_par","org","genere_le","contenu","resume","empreinte"],"type":"object"},"BundleOrg":{"properties":{"nom":{"type":"string"},"slug":{"type":"string"}},"required":["slug","nom"],"type":"object"},"CallbackFederationReq":{"properties":{"code":{"type":"string"},"erreur":{"description":"`error` renvoyé par l'IdP, le cas échéant.","type":["string","null"]},"erreur_description":{"type":["string","null"]},"state":{"type":"string"}},"required":["state","code"],"type":"object"},"CanariReq":{"description":"Corps de pose d'un canari sur une app.","properties":{"poids_pct":{"description":"Part du trafic du canari (1-99 %).","format":"int32","minimum":0,"type":"integer"},"revision_canari":{"description":"Id de la révision canari (reçoit poids_pct du trafic).","type":"string"},"revision_stable":{"description":"Id de la révision stable (reçoit le complément du trafic).","type":"string"}},"required":["revision_stable","revision_canari","poids_pct"],"type":"object"},"CasEval":{"properties":{"criteres":{"items":{"$ref":"#/components/schemas/Critere"},"type":"array"},"question":{"type":"string"}},"required":["question","criteres"],"type":"object"},"ChampPluginReq":{"properties":{"nom":{"description":"Nom du champ (identifiant 1-40 [A-Za-z0-9_], sans chiffre en tête).","type":"string"},"source":{"description":"La requête interne ou la vue métier qui alimente le champ.","type":"string"},"type":{"description":"Type FERMÉ : String | Int | Float | Boolean | JSON.","type":"string"}},"required":["nom","type","source"],"type":"object"},"ChangerPlanReq":{"properties":{"plan":{"description":"basic | argent | or.","type":"string"}},"required":["plan"],"type":"object"},"ChargeJetonApp":{"description":"La charge d'un jeton d'app : sujet, app, rôles, serial croissant,\nfenêtre de validité — TOUT est signé.","properties":{"app":{"description":"L'app pour laquelle le jeton vaut (et pour ELLE SEULE).","type":"string"},"emis_a":{"format":"int64","minimum":0,"type":"integer"},"expire_a":{"format":"int64","minimum":0,"type":"integer"},"roles":{"items":{"type":"string"},"type":"array"},"serial":{"description":"Numéro de série — strictement croissant par utilisateur (rotation).","format":"int64","minimum":0,"type":"integer"},"sujet":{"description":"`usr-…` — le sujet.","type":"string"}},"required":["sujet","app","roles","serial","emis_a","expire_a"],"type":"object"},"ChatCompletionsReq":{"properties":{"max_tokens":{"format":"int64","type":"integer"},"messages":{"items":{"$ref":"#/components/schemas/MessageChat"},"type":"array"},"model":{"type":["string","null"]},"stream":{"type":"boolean"}},"required":["messages"],"type":"object"},"ChercherRagReq":{"properties":{"k":{"minimum":0,"type":"integer"},"question":{"type":"string"},"seuil":{"format":"double","type":"number"}},"required":["question"],"type":"object"},"ChercherTagsReq":{"properties":{"filtre":{"description":"Filtre : « cle », « cle=valeur » ou vide (= tout)","type":"string"}},"required":["filtre"],"type":"object"},"ChiffrerLotsLogsReq":{"properties":{"cle_racine_base64":{"description":"Clé racine (32 octets en base64) — aucun keystore d'org ne la\nfournit à ce jour : exigée ici, refus honnête sinon.","type":["string","null"]}},"type":"object"},"ChiffrerSseReq":{"properties":{"cle_objet":{"description":"Clé de l'objet — le chiffré lui est lié par l'AAD (un chiffré\ndéplacé sous une autre clé ne s'authentifie pas).","type":"string"},"cle_racine_base64":{"description":"Clé racine (32 octets en base64) — aucun keystore d'org ne la\nfournit à ce jour : exigée ici, refus honnête sinon.","type":["string","null"]},"contenu_base64":{"description":"Contenu EN CLAIR de l'objet (base64).","type":"string"},"nonce_base64":{"description":"Nonce 96 bits (12 octets en base64), frais par objet — absent :\ngénéré ici et rendu dans le chiffré.","type":["string","null"]}},"required":["contenu_base64","cle_objet"],"type":"object"},"ChunkEdge":{"description":"Un chunk de fichier : indice, empreinte SHA-256, taille MESURÉE.","properties":{"empreinte":{"type":"string"},"indice":{"format":"int32","minimum":0,"type":"integer"},"octets":{"format":"int64","minimum":0,"type":"integer"}},"required":["indice","empreinte","octets"],"type":"object"},"CibleReplicationApiReq":{"description":"Miroir API d'une cible de réplication (noyau sans ToSchema).","properties":{"org":{"description":"Sous-dossier org cible.","type":"string"},"racine":{"description":"Racine froide cible (chemin absolu du NAS distant).","type":"string"}},"required":["racine","org"],"type":"object"},"CiblesCarteReq":{"properties":{"port":{"description":"Port appliqué à chaque adresse résolue.","format":"int32","minimum":0,"type":"integer"},"service":{"description":"Nom de service à résoudre dans la carte (ex. « api »).","type":"string"}},"required":["service","port"],"type":"object"},"CircuitFluxGitReq":{"description":"Circuit complet d'un push (fichiers du dépôt mesurés par l'appelant).","properties":{"branche":{"description":"Branche observée.","type":"string"},"depot":{"description":"Dépôt du flux déclaré.","type":"string"},"fichiers":{"description":"Manifeste du dépôt (noms à la racine) — mesuré côté dépôt.","items":{"type":"string"},"type":"array"},"procfile":{"description":"CONTENU du Procfile s'il existe.","type":["string","null"]}},"required":["depot","branche","fichiers"],"type":"object"},"ClasserTexteReq":{"description":"La demande de classification k-NN.","properties":{"exemples":{"description":"Exemples étiquetés (le classifieur ne connaît que ce qu'on\nlui montre).","items":{"$ref":"#/components/schemas/ExempleClasseReq"},"type":"array"},"k":{"description":"Voisins consultés (défaut 3).","minimum":0,"type":"integer"},"seuil":{"description":"Similarité minimale du plus proche voisin (défaut 0,15) — en\ndessous, « incertain » plutôt qu'une étiquette hasardeuse.","format":"double","type":"number"},"texte":{"type":"string"}},"required":["texte","exemples"],"type":"object"},"ClassifierReq":{"properties":{"texte":{"description":"Texte brut à classer (1 Mio max) — jamais persisté.","type":"string"}},"required":["texte"],"type":"object"},"CleS3":{"description":"Une clé d'accès enregistrée (jamais le secret).","properties":{"access_key":{"description":"Identifiant d'accès public (celui des exemples aws cli).","type":"string"},"actions":{"description":"Actions seaweed exactes, lisibles : « Read:logs,Write:logs,List ».","type":"string"},"cree_le":{"type":"string"},"etat":{"description":"active | revoquee","type":"string"},"id":{"type":"string"},"nom":{"type":"string"}},"required":["id","nom","access_key","actions","cree_le","etat"],"type":"object"},"CleS3Cree":{"description":"POST /v1/projects/{pid}/s3/cles — clé créée : le secret est rendu\nUNE SEULE FOIS dans cette réponse.","properties":{"access_key":{"description":"Identifiant d'accès public (celui des exemples aws cli).","type":"string"},"actions":{"description":"Actions seaweed lisibles : « Read:logs,Write:logs,List ».","type":"string"},"cree_le":{"type":"string"},"endpoint":{"type":"string"},"equivalents":{"description":"Équivalents CLI/OpenTofu de la création."},"etat":{"description":"active | revoquee","type":"string"},"id":{"type":"string"},"nom":{"type":"string"},"note":{"type":"string"},"secret_key":{"description":"Le secret — JAMAIS re-rendu ensuite (chiffré AES-256-GCM au\nrepos, réservé à la révocation/audit). Conservez-le maintenant.","type":"string"},"usage":{"description":"Mode d'emploi aws cli (access key préremplie).","type":"string"}},"required":["id","nom","access_key","actions","cree_le","etat","secret_key","note","endpoint","usage","equivalents"],"type":"object"},"CleS3Supprimee":{"description":"DELETE /v1/projects/{pid}/s3/cles/{id} — clé révoquée (identité\nretirée de la passerelle en premier).","properties":{"revoquee":{"description":"Id de la clé révoquée (s3k-…).","type":"string"}},"required":["revoquee"],"type":"object"},"ClesS3Liste":{"description":"GET /v1/projects/{pid}/s3/cles — clés d'accès S3 de l'org, JAMAIS\nles secrets (rendus une seule fois à la création).","properties":{"endpoint":{"description":"Passerelle S3 qui vérifie la Signature V4.","type":"string"},"items":{"items":{"$ref":"#/components/schemas/CleS3"},"type":"array"},"note":{"type":"string"}},"required":["items","endpoint","note"],"type":"object"},"ClonerVolumeReq":{"properties":{"snapshot_base":{"description":"Snapshot de base — doit exister ET appartenir à CE volume.","type":"string"}},"required":["snapshot_base"],"type":"object"},"ClusterFedereApiReq":{"description":"Clusters fournis pour l'évaluation fédérée (tout est mesuré par l'appelant).","properties":{"id":{"type":"string"},"nb_noeuds":{"format":"int64","type":"integer"},"nb_pods":{"description":"Pods relevés par l'appelant (mesuré, jamais déduit).","format":"int64","type":"integer"},"nom":{"type":"string"},"version":{"type":"string"}},"required":["id","nom","nb_pods","nb_noeuds","version"],"type":"object"},"ClustersFederesReq":{"description":"Corps de l'évaluation fédérée.","properties":{"clusters":{"items":{"$ref":"#/components/schemas/ClusterFedereApiReq"},"type":"array"}},"required":["clusters"],"type":"object"},"ColonneMeta":{"properties":{"colonne":{"type":"string"},"echantillon":{"description":"Jusqu'à 3 valeurs d'échantillon — l'appelant fournit les\nmétadonnées, cirroSensible ne se connecte PAS à la base.","items":{"type":"string"},"type":"array"},"table":{"type":"string"}},"required":["table","colonne","echantillon"],"type":"object"},"ComplexiteReq":{"properties":{"requete":{"description":"La requête GraphQL à juger (8 Kio max, profondeur 5 — bornes du parseur).","type":"string"}},"required":["requete"],"type":"object"},"ComposantReq":{"properties":{"nom":{"type":"string"},"version":{"format":"int32","minimum":0,"type":"integer"}},"required":["nom","version"],"type":"object"},"Composants":{"description":"Ce qu'une stack rassemble — au MOINS l'app : une stack sans app\nn'a rien à déployer, la validation la refuse (400 logique).","properties":{"app":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AppComp","description":"Obligatoire — le seul composant non optionnel."}]},"base":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BaseComp"}]},"cdn":{"description":"CDN devant le domaine — ignoré (et non facturé) sans domaine.","type":"boolean"},"domaine":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/DomaineComp"}]},"secret_injecte":{"description":"Injecter un secret à l'app (l'URI de la base, typiquement).","type":"boolean"}},"type":"object"},"CompteursABApi":{"description":"Miroir API des compteurs d'un bras — le noyau est sans ToSchema.","properties":{"appels":{"description":"Appels MESURÉS dans le bras.","format":"int64","minimum":0,"type":"integer"},"duree_totale_ms":{"description":"Durée cumulée (ms).","format":"int64","minimum":0,"type":"integer"},"erreurs":{"description":"Livraisons échouées.","format":"int64","minimum":0,"type":"integer"}},"required":["appels","erreurs","duree_totale_ms"],"type":"object"},"ConfigBasculeDns":{"description":"Configuration persistée (meta `infra:dns_bascule`).","properties":{"actif":{"type":"boolean"},"mappings":{"items":{"$ref":"#/components/schemas/MappingDns"},"type":"array"}},"type":"object"},"ConfigFederationReq":{"properties":{"client_id":{"type":"string"},"decouverte":{"description":"Document de découverte OIDC RÉEL tel que servi par l'IdP\n(issuer, endpoints, jwks_uri) — parsé refusant champ par champ."},"redirect_uri":{"type":"string"}},"required":["decouverte","client_id","redirect_uri"],"type":"object"},"ConfigPoolReq":{"description":"Corps de pose d'une configuration de pool (un runtime par pool).","properties":{"duree_froid_ms":{"description":"Durée MESURÉE d'un démarrage à froid du runtime (ms) — exigée > 0.","format":"int64","minimum":0,"type":"integer"},"inactivite_max_s":{"description":"Inactivité au-delà de laquelle une instance est éteinte (s).","format":"int64","minimum":0,"type":"integer"},"instances_max":{"description":"Instances chaudes au maximum.","minimum":0,"type":"integer"},"instances_min":{"description":"Instances chaudes conservées au minimum.","minimum":0,"type":"integer"}},"required":["instances_min","instances_max","inactivite_max_s","duree_froid_ms"],"type":"object"},"ConfirmerRecoReq":{"properties":{"preuve":{"description":"Comment l'économie a été constatée (ex. « vm-42 supprimée le… »).","type":"string"}},"required":["preuve"],"type":"object"},"ConnexionEdgeReq":{"properties":{"action":{"description":"couper | reconnecter — une coupure est un fait mesuré, pas une rafale.","type":"string"}},"required":["action"],"type":"object"},"ConsoleRep":{"description":"Réponse de POST /v1/console — code de sortie RÉEL du binaire, sortie\nhonnête (jamais fabriquée), « tronque » seulement si le plafond a mordu.","properties":{"code":{"description":"Code de sortie du processus (124 = délai 10 s dépassé, 127 = lancement impossible).","format":"int32","type":"integer"},"sortie":{"description":"stdout puis stderr du processus, plafonnée à 64 Kio.","type":"string"},"tronque":{"description":"true quand la sortie a été coupée au plafond de 64 Kio.","type":"boolean"}},"required":["code","sortie"],"type":"object"},"ConsoleReq":{"description":"Corps de POST /v1/console.","properties":{"commande":{"description":"Ligne de commande cirro SANS le binaire (ex. « org vue », « --version »).","type":"string"},"org":{"description":"Organisation active côté WebUI — contrôlée project.read ; à défaut\nla première organisation du compte fait contexte.","type":["string","null"]}},"required":["commande"],"type":"object"},"ConsommerKvReq":{"properties":{"consommateur":{"description":"Nom du consommateur (alphanumérique, - _ ., 1-40) — son curseur.","type":"string"},"max":{"minimum":0,"type":"integer"}},"required":["consommateur"],"type":"object"},"ConsommerPontKafkaReq":{"properties":{"curseur":{"description":"Offset du dernier message CONSOMMÉ (sémantique Kafka : reprise\nstrictement après).","format":"int64","minimum":0,"type":"integer"}},"type":"object"},"ConsommerPontReq":{"properties":{"max":{"description":"Maximum d'enregistrements du lot (plafonné à MAX_LECTURE).","minimum":0,"type":"integer"}},"type":"object"},"ConsulterCacheReq":{"description":"Corps de consultation du cache pour un préfixe.","properties":{"modele":{"description":"Modèle (clé du cache avec le préfixe).","type":"string"},"prefixe":{"description":"Le préfixe de prompt déjà calculé (system + few-shots…).","type":"string"}},"required":["modele","prefixe"],"type":"object"},"ContactReq":{"properties":{"email":{"type":"string"},"nom":{"type":"string"},"prenom":{"type":"string"},"tel":{"type":"string"}},"required":["nom","prenom","email","tel"],"type":"object"},"ContexteRequeteReq":{"properties":{"chemin":{"type":"string"},"cookies":{"items":{"type":"string"},"type":"array"},"entetes":{"additionalProperties":{"type":"string"},"propertyNames":{"type":"string"},"type":"object"},"query":{"items":{"type":"string"},"type":"array"}},"required":["chemin"],"type":"object"},"Contrainte":{"oneOf":[{"description":"Types de VM autorisés (ex. [\"cs-small\", \"cs-medium\"]).","properties":{"genre":{"enum":["types_vm_autorises"],"type":"string"},"types":{"items":{"type":"string"},"type":"array"}},"required":["types","genre"],"type":"object"},{"description":"Zone géographique obligatoire (ex. \"nantes\").","properties":{"genre":{"enum":["zone_obligatoire"],"type":"string"},"zone":{"type":"string"}},"required":["zone","genre"],"type":"object"},{"description":"Tags requis sur toute ressource (ex. [\"env\", \"team\"]).","properties":{"cles":{"items":{"type":"string"},"type":"array"},"genre":{"enum":["tags_requis"],"type":"string"}},"required":["cles","genre"],"type":"object"},{"description":"Préfixe imposé sur les noms de VM (ex. \"prod-\").","properties":{"genre":{"enum":["prefixe_nom_vm"],"type":"string"},"prefixe":{"type":"string"}},"required":["prefixe","genre"],"type":"object"},{"description":"Coût horaire maximum par VM (€).","properties":{"genre":{"enum":["cout_max_vm"],"type":"string"},"max_h":{"format":"double","type":"number"}},"required":["max_h","genre"],"type":"object"},{"description":"Nombre maximum de machines Proxmox réelles.","properties":{"genre":{"enum":["machines_max"],"type":"string"},"max":{"minimum":0,"type":"integer"}},"required":["max","genre"],"type":"object"},{"description":"Images OS autorisées (ex. [\"debian-12\"]).","properties":{"genre":{"enum":["images_autorisees"],"type":"string"},"images":{"items":{"type":"string"},"type":"array"}},"required":["images","genre"],"type":"object"}]},"ContratEtapeApiReq":{"description":"Corps API d'un contrat d'étape conteneurisée (miroir ToSchema de\n[`crate::dataflow::ContratEtape`]).","properties":{"entree_colonnes":{"description":"Colonnes exigées en entrée — vide = libre.","items":{"type":"string"},"type":"array"},"entree_format":{"description":"csv | json | jsonl.","type":"string"},"sortie_colonnes":{"description":"Colonnes promises en sortie — le contrat vérifiable.","items":{"type":"string"},"type":"array"},"sortie_format":{"description":"csv | json | jsonl.","type":"string"}},"required":["entree_format","sortie_format","sortie_colonnes"],"type":"object"},"CoutRessources":{"description":"Réponse complète : lignes triées par coût décroissant, total = somme\nexacte des lignes, et les familles présentes mais SANS tarif.","properties":{"familles_non_tarifees":{"description":"Familles vivantes sans aucun tarif défini — hors total, nommées\nhonnêtement au lieu de recevoir un prix inventé.","items":{"type":"string"},"type":"array"},"lignes":{"items":{"$ref":"#/components/schemas/LigneCoutRessource"},"type":"array"},"total":{"format":"double","type":"number"}},"required":["lignes","total"],"type":"object"},"CreerEtatReq":{"properties":{"fichiers":{"description":"Arborescence capturée : chemins, tailles et empreintes MESURÉS.","items":{"$ref":"#/components/schemas/FichierEtat"},"type":"array"},"nom":{"description":"Nom lisible (ex. « avant-migration »), unique par workspace.","type":"string"}},"required":["nom"],"type":"object"},"CreerGroupeFeaturesReq":{"properties":{"description":{"type":"string"},"features":{"items":{"type":"string"},"type":"array"},"nom":{"type":"string"}},"required":["nom","features"],"type":"object"},"CreerPreviewReq":{"properties":{"branche":{"description":"Branche en slug [a-z0-9-] — JAMAIS main ni master.","type":"string"}},"required":["branche"],"type":"object"},"CreerSessionPortailReq":{"properties":{"email":{"type":"string"},"jeton":{"description":"Jeton choisi par l'intégrateur (≥ 8 caractères).","type":"string"},"role":{"description":"lecteur | dev | admin.","type":"string"}},"required":["jeton","email","role"],"type":"object"},"CreerSnapshotNfsReq":{"properties":{"fichiers":{"description":"Relevé de l'arborescence (chemins relatifs, tailles MESURÉES).","items":{"$ref":"#/components/schemas/FichierSnapshotReq"},"type":"array"},"nom":{"description":"Nom lisible horodaté (ex. « 2026-09-30T22:00 ») — répertoire\nsous la racine cachée .snapshots de l'export.","type":"string"}},"required":["nom"],"type":"object"},"Critere":{"properties":{"genre":{"description":"contient | exact | regex | outil_utilise.","type":"string"},"negatif":{"description":"Vrai = le critère réussit quand la condition est FAUSSE.","type":"boolean"},"valeur":{"type":"string"}},"required":["genre","valeur"],"type":"object"},"CronJob":{"properties":{"commande":{"description":"Commande réellement exécutée sur la cible (serveur_id ou\nmachine_id) à chaque déclenchement ; vide = pur déclencheur\n(audit + webhooks seulement). Anomalie 5 audit1 : ce champ\nétait stocké mais jamais exécuté.","type":"string"},"created_at":{"type":"string"},"deleted_at":{"type":["string","null"]},"dernier_code_sortie":{"description":"Trace de la dernière exécution (code de sortie + extraits\ntronqués) — uniquement quand `commande` est posée.","format":"int64","type":["integer","null"]},"dernier_stderr":{"type":["string","null"]},"dernier_stdout":{"type":["string","null"]},"derniere_execution":{"type":["string","null"]},"expression":{"description":"Expression cron 5 champs : \"*/5 * * * *\"","type":"string"},"id":{"type":"string"},"machine_id":{"description":"Cible d'exécution : machine PVE (pmx-…) via pct exec","type":["string","null"]},"nom":{"type":"string"},"project_id":{"type":"string"},"serveur_id":{"description":"Cible d'exécution : serveur externe (srv-…) via SSH","type":["string","null"]}},"required":["id","project_id","nom","expression","commande","created_at"],"type":"object"},"DagReq":{"description":"Corps de pose des arcs du DAG (paires [prerequis, dependent]).","properties":{"arcs":{"items":{"items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":"array"},"type":"array"}},"required":["arcs"],"type":"object"},"DashboardReq":{"description":"Corps de génération d'un dashboard.","properties":{"ressource":{"description":"Ressource nommée par le dashboard (le titre en dérive).","type":"string"},"series":{"items":{"$ref":"#/components/schemas/SerieDeclareeReq"},"type":"array"}},"required":["ressource","series"],"type":"object"},"DebutExamenReq":{"properties":{"cursus":{"type":"string"},"niveau":{"type":"string"},"nom":{"type":"string"},"prenom":{"type":"string"}},"required":["cursus","niveau","nom","prenom"],"type":"object"},"DechiffrerSseReq":{"properties":{"cle_racine_base64":{"description":"Clé racine (32 octets en base64) — aucun keystore d'org ne la\nfournit à ce jour : exigée ici, refus honnête sinon.","type":["string","null"]},"objet":{"$ref":"#/components/schemas/ObjetChiffreApi","description":"L'objet chiffré tel que rendu par chiffrer."}},"required":["objet"],"type":"object"},"Decision":{"description":"Décision du pare-feu pour une requête.","properties":{"ban_pose":{"description":"Some(motif) quand CETTE évaluation a posé un NOUVEAU ban\nautomatique (franchissement de seuil) — le middleware s'en sert\npour alimenter cirroGarde (anomalie parefeu_ban). Un verdict\n« bannie » sur un ban posé AVANT ce coup garde None (pas de\ndoublon d'anomalie à chaque requête d'une IP déjà bannie).","type":["string","null"]},"motif":{"type":"string"},"regle":{"description":"Règle L7 déclenchée (si verdict bloquee_l7).","type":["string","null"]},"score":{"format":"double","type":"number"},"verdict":{"description":"autorisee | bannie | limitee | bloquee_l7","type":"string"}},"required":["verdict","motif","score"],"type":"object"},"DecisionAutopilotReq":{"description":"État de nœuds du pool, mesuré par l'appelant.","properties":{"noeuds_actuels":{"description":"Nœuds ACTUELS du pool implicite — mesurés, jamais devinés.","format":"int32","minimum":0,"type":"integer"}},"required":["noeuds_actuels"],"type":"object"},"DecisionMeshReq":{"description":"Corps d'une décision de routage inter-site.","properties":{"destination_ip":{"description":"IPv4 destination.","type":"string"},"source":{"description":"Site source (doit exister dans la topologie).","type":"string"}},"required":["source","destination_ip"],"type":"object"},"DecisionReq":{"properties":{"approuver":{"type":"boolean"},"motif":{"type":["string","null"]}},"required":["approuver"],"type":"object"},"DeclencheurApiReq":{"description":"Miroir API d'un déclencheur (le noyau est sans ToSchema).","properties":{"cible":{"description":"Chemin absolu (http), id rgl-… (regle), id top-… (topic), expression cron 5 champs.","type":"string"},"filtre_sujet":{"description":"Topic uniquement : préfixe de sujet exigé (vide = tout).","type":["string","null"]},"fonction_id":{"description":"Fonction appelée quand le déclencheur s'enflamme (fn-…).","type":"string"},"genre":{"description":"http | regle | topic | cron.","type":"string"}},"required":["fonction_id","genre","cible"],"type":"object"},"DecouvrirReq":{"properties":{"base":{"description":"base du /24 sans le suffixe, ex. « 192.168.1 »","type":"string"}},"required":["base"],"type":"object"},"DefautFeReq":{"properties":{"statut":{"description":"accept | reject","type":"string"}},"required":["statut"],"type":"object"},"DefinitionFeature":{"description":"Une feature calculée DÉCLARÉE : elle écoute un genre d'événement,\nagrège sur une fenêtre glissante, se publie dans un groupe du\nmagasin en ligne.","properties":{"agregation":{"$ref":"#/components/schemas/Agregation"},"fenetre_s":{"description":"Fenêtre glissante (s) ; 0 = sans fenêtre (tout l'historique retenu).","format":"int64","minimum":0,"type":"integer"},"groupe":{"description":"Groupe du magasin en ligne où publier le résultat.","type":"string"},"nom":{"description":"Nom de la feature calculée ([a-z0-9_] 1-40).","type":"string"},"source":{"description":"Genre d'événement écouté ([a-z0-9_] 1-40).","type":"string"}},"required":["nom","source","fenetre_s","agregation","groupe"],"type":"object"},"DefinitionQuotasReq":{"properties":{"disque_gb":{"format":"int64","type":["integer","null"]},"nb_vms":{"format":"int64","type":["integer","null"]},"ram_mb":{"format":"int64","type":["integer","null"]},"vcpu":{"format":"int64","type":["integer","null"]}},"type":"object"},"DeltaEdgeReq":{"properties":{"courante":{"$ref":"#/components/schemas/VersionEdge","description":"Version COURANTE du device."},"nouvelle":{"$ref":"#/components/schemas/VersionEdge","description":"Version NOUVELLE publiée par le cloud (composant, numéro, fichiers)."}},"required":["nouvelle","courante"],"type":"object"},"DemandeCouvertureReq":{"properties":{"genre":{"description":"app | zone_dns | topic.","type":"string"},"id":{"type":"string"}},"required":["genre","id"],"type":"object"},"DemandeDevis":{"properties":{"ressource":{"type":"string"},"type_vm":{"description":"Gabarit du catalogue cs-*.","type":"string"}},"required":["ressource","type_vm"],"type":"object"},"DemandeQuota":{"properties":{"champ":{"description":"vcpu | ram_mb | disque_gb | nb_vms","type":"string"},"created_at":{"type":"string"},"demandeur":{"type":"string"},"etat":{"description":"en_attente | approuvee | refusee","type":"string"},"id":{"type":"string"},"justification":{"type":"string"},"motif_decision":{"type":["string","null"]},"org":{"type":"string"},"traitee_a":{"type":["string","null"]},"traitee_par":{"type":["string","null"]},"valeur_actuelle":{"format":"int64","type":"integer"},"valeur_demandee":{"format":"int64","type":"integer"}},"required":["id","org","demandeur","champ","valeur_demandee","valeur_actuelle","justification","etat","created_at"],"type":"object"},"DemandeRunWebhookReq":{"description":"La porte ENTRANTE : corps = la demande du module (agent visé, clé,\nquestion) — chaque refus est nommée pour l'appelant.","properties":{"agent":{"type":"string"},"cle":{"description":"Clé du déclencheur (vérifiée puis jetée).","type":"string"},"question":{"description":"La question qui devient le run (≤ 2 000 caractères).","type":"string"}},"required":["agent","cle","question"],"type":"object"},"DemarrageWarmReq":{"description":"Consigne le démarrage décidé par une invocation `Demarrer`.","properties":{"instance_id":{"description":"Id de l'instance chaude née du démarrage (1-64 caractères).","type":"string"},"maintenant":{"description":"Secondes epoch — fournie par l'appelant.","format":"int64","minimum":0,"type":"integer"}},"required":["instance_id","maintenant"],"type":"object"},"DeriveReq":{"properties":{"fenetre":{"description":"Fenêtre courante MESURÉE (empreintes récentes).","items":{"$ref":"#/components/schemas/EchantillonApi"},"type":"array"},"reference":{"description":"Référentiel MESURÉ (empreintes d'une période de référence).","items":{"$ref":"#/components/schemas/EchantillonApi"},"type":"array"},"seuil":{"description":"Seuil de divergence Jensen-Shannon, dans [0,1].","format":"double","type":"number"}},"required":["reference","fenetre","seuil"],"type":"object"},"Destination":{"description":"Destination d'une règle.","properties":{"cible":{"description":"webhook : URL http(s)://… · topic : identifiant ou nom du topic.","type":"string"},"genre":{"description":"\"webhook\" (POST HTTP signé) | \"topic\" (fan-out cirroTopics).","type":"string"}},"required":["genre","cible"],"type":"object"},"DetectionRuntimeReq":{"description":"Manifeste du dépôt (noms de fichiers à la racine).","properties":{"fichiers":{"description":"Noms EXACTS des fichiers à la racine (jamais le contenu).","items":{"type":"string"},"type":"array"}},"required":["fichiers"],"type":"object"},"DevisReq":{"properties":{"demandes":{"description":"Couples ressource → gabarit (≤ 50 lignes par devis).","items":{"$ref":"#/components/schemas/DemandeDevis"},"type":"array"}},"required":["demandes"],"type":"object"},"DiffCapturesReq":{"properties":{"apres":{"description":"`pris_le` (RFC 3339) de la capture comparée.","type":"string"},"avant":{"description":"`pris_le` (RFC 3339) de la capture de référence.","type":"string"}},"required":["avant","apres"],"type":"object"},"DiffImagesReq":{"properties":{"a_base64":{"type":"string"},"b_base64":{"type":"string"},"blocs_max":{"description":"Blocs comparés (défaut 1024, max BLOCS_DIFF_MAX).","minimum":0,"type":"integer"}},"required":["a_base64","b_base64"],"type":"object"},"DireReq":{"properties":{"texte":{"type":"string"}},"required":["texte"],"type":"object"},"DomaineComp":{"description":"Le composant domaine : un sous-domaine posé sur une zone DNS\nexistante de l'org — l'existence du slug est vérifiée à\nl'intégration, la forme ici.","properties":{"sous_domaine":{"description":"1-63 caractères, [a-z0-9-], sans tiret en bord.","type":"string"},"zone":{"description":"Slug de zone existante (ex. « lab-fr ») — exigé réel à l'intégration.","type":"string"}},"required":["zone","sous_domaine"],"type":"object"},"DonneesExport":{"description":"Section `donnees` du bundle v2 : objets S3 + secrets chiffrés.","properties":{"bornes":{"description":"Bornes appliquées — dites dans le bundle, jamais cachées."},"buckets_limites":{"description":"Buckets touchés par la limite (200 objets / 50 Mo).","items":{"type":"string"},"type":"array"},"note_secrets":{"description":"Note explicite quand les secrets ne partent pas (pas de\npassphrase fournie à l'export).","type":["string","null"]},"objets_s3":{"additionalProperties":{"items":{"$ref":"#/components/schemas/ObjetS3Export"},"type":"array"},"description":"bucket → objets embarqués (buckets triés, clés triées).","propertyNames":{"type":"string"},"type":"object"},"secrets_chiffres":{"description":"Secrets chiffrés par la passphrase (vide si aucune passphrase).","items":{"$ref":"#/components/schemas/SecretChiffre"},"type":"array"},"tronque":{"description":"Plafond de collecte atteint : inventaire PARTIEL.","type":"boolean"}},"required":["objets_s3","tronque","buckets_limites","bornes","secrets_chiffres"],"type":"object"},"DroitS3":{"description":"Un droit demandé : action (Read/Write/List/Tagging/Admin) et,\npour Read/Write/Tagging, le bucket visé — `None` = TOUS les\nbuckets de l'org (documenté : l'espace de noms de la passerelle\npartagée est PLAT, un droit global lit tout bucket homonyme).","properties":{"action":{"description":"Read | Write | List | Tagging | Admin","type":"string"},"bucket":{"description":"Bucket visé — requis pour Read/Write/Tagging, ignoré pour\nList/Admin (globaux par nature).","type":["string","null"]}},"required":["action"],"type":"object"},"EchangerFederationReq":{"properties":{"reponse":{"description":"Réponse du token endpoint de l'IdP (access_token + id_token JWT)\n— VALIDÉE claim par claim (iss, aud, nonce, exp mesuré), puis la\nSIGNATURE vérifiée pour de vrai contre la JWKS de l'émetteur\n(RS256/ES256, fetch réel du jwks_uri)."}},"required":["reponse"],"type":"object"},"EchangerReservationReq":{"properties":{"delta_q":{"description":"Delta de quantité — négatif admis jusqu'à −quantité.","format":"int32","type":"integer"},"nouveau_gabarit":{"description":"Nouveau gabarit du catalogue (cs-…).","type":"string"}},"required":["nouveau_gabarit","delta_q"],"type":"object"},"EchantillonApi":{"description":"Miroir API d'un échantillon — le noyau est sans ToSchema.","properties":{"empreinte":{"description":"Empreinte POSITIVE produite par le moteur (histogramme\nnormalisable — l'extraction d'embedding appartient au moteur).","items":{"format":"float","type":"number"},"type":"array"},"ts":{"description":"Horodatage unix de la réponse.","format":"int64","type":"integer"}},"required":["ts","empreinte"],"type":"object"},"EchantillonCdnReq":{"properties":{"duree_ms":{"format":"int64","minimum":0,"type":"integer"},"hit":{"type":"boolean"},"octets":{"format":"int64","minimum":0,"type":"integer"}},"required":["hit","octets","duree_ms"],"type":"object"},"EchantillonRejeuReq":{"properties":{"requetes":{"description":"Historique MESURÉ (question + réponse de production).","items":{"$ref":"#/components/schemas/RequeteHistReq"},"type":"array"},"taille":{"minimum":0,"type":"integer"}},"required":["requetes","taille"],"type":"object"},"EchantillonReq":{"description":"Échantillon (horodatage RFC3339, valeur).","properties":{"ts":{"type":"string"},"valeur":{"format":"double","type":"number"}},"required":["ts","valeur"],"type":"object"},"EchantillonsReq":{"description":"Corps d'apport d'échantillons MESURÉS pour une métrique.","properties":{"valeurs":{"description":"Valeurs mesurées, les plus récentes en dernier.","items":{"format":"double","type":"number"},"type":"array"}},"required":["valeurs"],"type":"object"},"EmettreJetonAppReq":{"properties":{"ttl_s":{"description":"TTL en secondes (1 h par défaut, 30 jours max).","format":"int64","minimum":0,"type":"integer"},"utilisateur":{"description":"L'utilisateur (`usr-…`) — un utilisateur désactivé n'obtient rien.","type":"string"}},"required":["utilisateur"],"type":"object"},"EnregistrerReq":{"properties":{"cle":{"description":"Clé de partition — les enregistrements d'une même clé gardent\nleur ordre (la partition est stable).","type":"string"},"donnees":{"description":"Charge utile libre (JSON)."}},"required":["cle","donnees"],"type":"object"},"Entree":{"description":"Une ligne du journal d'exécution (franchissement, échec, branche…).","properties":{"quand":{"type":"string"},"quoi":{"type":"string"}},"required":["quand","quoi"],"type":"object"},"EntreeEtat":{"description":"Une ligne du dictionnaire : l'état brut d'un genre et son libellé.","properties":{"etat":{"description":"État brut tel qu'émis par le sous-système (jamais reformulé).","type":"string"},"etat_libelle":{"description":"Libellé canonique : demarree | arretee | en_attente | erreur | inconnu.","type":"string"},"genre":{"description":"Genre de ressource : instances | vms | apps | workspaces | k8s |\nnode_pools | machines.","type":"string"},"source":{"description":"Où l'état est émis/attendu (fichier:ligne au moment de la collecte).","type":"string"}},"required":["genre","etat","etat_libelle","source"],"type":"object"},"EntreeJournalApiReq":{"description":"Miroir API d'une entrée de journal (noyau sans ToSchema).","properties":{"genre":{"description":"insert | update | delete.","type":"string"},"ligne":{"description":"Identifiant de ligne dans la table.","type":"string"},"lsn":{"description":"Numéro de séquence — croissant, unique.","format":"int64","minimum":0,"type":"integer"},"table":{"type":"string"},"ts":{"description":"RFC3339 de l'écriture.","type":"string"}},"required":["lsn","ts","genre","table","ligne"],"type":"object"},"EntreeScimSerde":{"description":"Entrée d'annuaire SCIM persistable (le noyau ne sérialise pas —\nl'enveloppe vit ici, les sémantiques au noyau).","properties":{"actif":{"type":"boolean"},"courriel":{"type":"string"},"nom_affiche":{"type":"string"},"role":{"type":"string"}},"required":["courriel","nom_affiche","role","actif"],"type":"object"},"EnvoyerWebhookReq":{"properties":{"url":{"description":"URL réceptrice http(s) du webhook.","type":"string"}},"required":["url"],"type":"object"},"ErreurProblem":{"description":"Erreur produit RFC 9457 (application/problem+json) — forme EXACTE\nservie par `AppError::into_response` (error.rs) : mêmes champs,\nmêmes noms, tout du long de l'API.","properties":{"code":{"description":"Code stable pour les automatisations (ex. BUCKET_INCONNU).","type":"string"},"detail":{"description":"Explication en français clair.","type":"string"},"status":{"description":"Code HTTP servi (400, 401, 403…).","format":"int32","minimum":0,"type":"integer"},"title":{"description":"Titre lisible du statut HTTP (ex. « Requête invalide »).","type":"string"},"trace_id":{"description":"Trace copiable pour le support (régénérée à chaque réponse).","type":"string"},"type":{"description":"URI du type d'erreur : https://cirrostratus.internal/erreurs/{code}.","type":"string"}},"required":["type","title","status","detail","code","trace_id"],"type":"object"},"Etape":{"description":"Une étape du workflow. Les champs utiles dépendent de `action` :\nwebhook (cible = URL, charge = corps), fonction (cible = nom,\ncharge = corps), topic (cible = nom, sujet + message), attendre\n(secondes), condition (si_contient + alors/sinon).","properties":{"action":{"description":"\"webhook\" | \"fonction\" | \"topic\" | \"attendre\" | \"condition\".","type":"string"},"alors":{"description":"condition : prochaine étape si le texte est trouvé.","type":"string"},"backoff_sec":{"description":"Backoff PROPRE à l'étape entre tentatives (secondes ; 0 = délai\nscheduler standard [`REDELAI_TENTATIVE_SEC`]). La politique\nd'erreur devient granulaire, étape par étape (FLUX V, action 6).","format":"int64","minimum":0,"type":"integer"},"charge":{"description":"webhook/fonction : corps envoyé (texte libre, envoyé tel quel).","type":["string","null"]},"cible":{"description":"webhook : URL http(s) · fonction : nom de la fonction · topic : nom du topic.","type":"string"},"erreur_vers":{"description":"Catch (FLUX V, action 6) : étape de branchement quand CETTE\nétape échoue définitivement (tentatives épuisées) — l'exécution\ncontinue par là au lieu d'échouer. Vide = comportement\nhistorique : l'exécution entière échoue.","type":"string"},"message":{"description":"topic : message publié (JSON libre)."},"nom":{"description":"Identifiant d'étape, unique dans le workflow (cible des branches).","type":"string"},"secondes":{"description":"attendre : nombre de secondes (1..=3600).","format":"int64","minimum":0,"type":"integer"},"si_contient":{"description":"condition : texte cherché dans la réponse de l'étape précédente.","type":"string"},"sinon":{"description":"condition : prochaine étape sinon (vide = fin du workflow).","type":"string"},"sujet":{"description":"topic : préfixe de sujet de la publication.","type":"string"},"tentatives":{"description":"Tentatives en cas d'échec (1..=5, défaut 1).","format":"int32","minimum":0,"type":"integer"}},"required":["nom","action"],"type":"object"},"EtapeConteneurApiReq":{"description":"Corps API d'une étape conteneurisée (miroir ToSchema de\n[`crate::dataflow::EtapeConteneur`]).","properties":{"args":{"items":{"type":"string"},"type":"array"},"contrat":{"$ref":"#/components/schemas/ContratEtapeApiReq"},"image":{"description":"Image ÉTIQUETÉE — « ghcr.io/lab/etl:1.4.0 », « :latest » refusé.","type":"string"},"nom":{"description":"1-40 caractères.","type":"string"}},"required":["nom","image","args","contrat"],"type":"object"},"EtapeEtlApiReq":{"description":"Corps API d'une étape ETL (miroir plat de l'enum\n[`crate::dataflow::EtapeEtl`]) : `genre` fait l'aiguillage.","properties":{"colonne":{"description":"filtrer/trier : colonne visée.","type":["string","null"]},"colonnes":{"description":"projeter : colonnes gardées, dans cet ordre.","items":{"type":"string"},"type":["array","null"]},"desc":{"description":"trier : descendant si vrai.","type":["boolean","null"]},"genre":{"description":"filtrer | projeter | trier.","type":"string"},"operateur":{"description":"filtrer : =, !=, >, >=, <, <=.","type":["string","null"]},"valeur":{"description":"filtrer : valeur comparée (numérique si les deux s'y prêtent).","type":["string","null"]}},"required":["genre"],"type":"object"},"EtapesConteneurReq":{"description":"Corps de pose des étapes conteneurisées d'un pipeline.","properties":{"etapes":{"items":{"$ref":"#/components/schemas/EtapeConteneurApiReq"},"type":"array"}},"required":["etapes"],"type":"object"},"EtatOrgReq":{"description":"Vue d'ensemble : tous les clients, leurs quotas, leur usage réel,\nleurs machines, instances, IPs IPv6 et demandes en attente.","properties":{"etat":{"description":"« active » | « suspendue ».","type":"string"}},"required":["etat"],"type":"object"},"EtatPontReq":{"properties":{"actif":{"description":"true = reprendre la consommation, false = suspendre.","type":"boolean"}},"required":["actif"],"type":"object"},"Eval":{"properties":{"agent":{"type":"string"},"cas":{"items":{"$ref":"#/components/schemas/CasEval"},"type":"array"},"cree_le":{"type":"string"},"executions":{"items":{"$ref":"#/components/schemas/ExecutionEval"},"type":"array"},"id":{"type":"string"},"nom":{"type":"string"}},"required":["id","nom","agent","cas","executions","cree_le"],"type":"object"},"EvaluerReglesIotReq":{"properties":{"payload":{"description":"Payload JSON du message."},"topic":{"description":"Topic du message évalué (informatif — aucune restriction ici).","type":"string"}},"required":["payload"],"type":"object"},"EvaluerTieringReq":{"description":"Corps d'évaluation du tiering d'un bucket.","properties":{"objets":{"description":"Objets du bucket avec leur dernier accès MESURÉ (None = âge non mesuré).","items":{"$ref":"#/components/schemas/ObjetAvecAccesApiReq"},"type":"array"}},"required":["objets"],"type":"object"},"EvenementChronoReq":{"properties":{"evenement":{"type":"string"},"ts":{"type":"string"}},"required":["ts","evenement"],"type":"object"},"EvenementEntrantReq":{"description":"Événement entrant pour le test à blanc.","properties":{"genre":{"description":"http | regle | topic | cron.","type":"string"},"source":{"description":"Chemin sollicité, id de règle, id de topic…","type":"string"},"sujet":{"description":"Sujet de la publication (topics) — vide pour les autres genres.","type":"string"}},"required":["genre","source"],"type":"object"},"EvenementFlowReq":{"properties":{"destination":{"type":"string"},"octets":{"format":"int64","minimum":0,"type":"integer"},"paquets":{"format":"int64","minimum":0,"type":"integer"},"port":{"format":"int32","minimum":0,"type":["integer","null"]},"protocole":{"description":"tcp | udp | icmp","type":"string"},"source":{"type":"string"}},"required":["source","destination","protocole","octets","paquets"],"type":"object"},"EvenementMatrix":{"description":"Un événement du côté Matrix — seul m.room.message/m.text se\ntraduit, le reste est refusé nommé.","properties":{"contenu":{"description":"Contenu m.room.message : `{\"msgtype\":\"m.text\",\"body\":\"…\"}`."},"emetteur":{"description":"Émetteur Matrix (@user:domaine) s'il est connu.","type":["string","null"]},"genre":{"description":"Type de l'événement (« m.room.message » pour ce pont).","type":"string"},"salon":{"type":"string"},"ts":{"type":"string"}},"required":["genre","salon","contenu","ts"],"type":"object"},"EvenementSourceReq":{"properties":{"acteur":{"description":"IP qui échoue en SSH, domaine demandé en DNS.","type":"string"},"detail":{"description":"Détail brut (« auth refused », type de requête…).","type":"string"},"source":{"description":"ssh | dns.","type":"string"},"ts":{"description":"RFC3339 — lisible ou l'événement est exclu honnêtement.","type":"string"}},"required":["source","acteur","ts"],"type":"object"},"ExceptionConfigReq":{"description":"La demande d'exception planifiée.","properties":{"au":{"description":"RFC 3339 de la fin (exclue).","type":"string"},"du":{"description":"RFC 3339 du début (inclus).","type":"string"},"motif":{"description":"Pourquoi cette pause (≥ 5 caractères).","type":"string"},"regle_id":{"description":"Règle couverte.","type":"string"},"ressource_id":{"description":"Ressource précise — absent = toute la règle.","type":["string","null"]}},"required":["regle_id","motif","du","au"],"type":"object"},"ExecuterCommandeServeurReq":{"description":"Corps de POST /serveurs/{sid}/executer — le handler attend\n`commande` (exécution SSH). Nom UNIQUE : l'ancien nom\n`ExecuterReq` entrait en collision avec `athena::ExecuterReq`\n(champ `sql`) dans components(schemas) et la spec documentait\ndonc `sql` pour cette route → 422 pour qui suivait la doc\n(anomalie 11, ETAT-CLUSTER-audit1-2026-09-15).","properties":{"commande":{"description":"commande à exécuter sur le serveur distant (obligatoire)","type":"string"}},"required":["commande"],"type":"object"},"ExecuterExerciceReq":{"description":"Corps d'exécution d'un exercice blanc.","properties":{"mappings":{"description":"Mappings DNS exercés (records A recalculés sur COPIES).","items":{"$ref":"#/components/schemas/MappingDns"},"type":"array"},"sante":{"description":"Santé MESURÉE des sites (au moins deux peuplés, sinon rien à opposer).","items":{"$ref":"#/components/schemas/SanteSiteReq"},"type":"array"},"site_actif":{"description":"Site actif actuel (celui qu'on coupe DANS LA SIMULATION).","type":"string"},"zone_id":{"description":"Zone DNS dont les records servent de support (copie).","type":"string"}},"required":["site_actif","zone_id","sante"],"type":"object"},"ExecuterMultiReq":{"description":"Corps d'exécution d'un pipeline multi : les données de CHAQUE\nsource sont APPORTÉES (l'exécution pure ne lit ni SQL ni HTTP).","properties":{"donnees":{"additionalProperties":{"items":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"type":"array"},"description":"Une entrée par nom de source : lignes objets JSON.","propertyNames":{"type":"string"},"type":"object"}},"required":["donnees"],"type":"object"},"ExecuterReq":{"properties":{"sql":{"type":"string"}},"required":["sql"],"type":"object"},"ExecutionEval":{"properties":{"cout_euros":{"format":"double","type":"number"},"cree_le":{"type":"string"},"duree_ms":{"format":"int64","minimum":0,"type":"integer"},"id":{"type":"string"},"resultats":{"items":{"$ref":"#/components/schemas/ResultatCas"},"type":"array"},"score_pct":{"format":"double","type":"number"}},"required":["id","cree_le","score_pct","resultats","cout_euros","duree_ms"],"type":"object"},"ExecutionMicroLotReq":{"description":"Une exécution de micro-lot inscrite au journal (les trois\nhorodatages sont APPORTÉS par l'exécuteur qui les a mesurés).","properties":{"declenche_a":{"type":"string"},"nb_lignes":{"minimum":0,"type":"integer"},"ok":{"type":"boolean"},"prevu_a":{"type":"string"},"termine_a":{"type":"string"}},"required":["prevu_a","declenche_a","termine_a","nb_lignes"],"type":"object"},"Exemple":{"properties":{"instruction":{"description":"Consigne donnée au modèle.","type":"string"},"reponse":{"description":"Réponse attendue.","type":"string"}},"required":["instruction","reponse"],"type":"object"},"ExempleClasseReq":{"properties":{"etiquette":{"type":"string"},"texte":{"type":"string"}},"required":["texte","etiquette"],"type":"object"},"ExpressionReq":{"properties":{"expression":{"description":"Expression du mini-langage : comparaisons « attribut op valeur »\nreliées par ET (prioritaire) et OU, SANS parenthèses.","type":"string"},"ressource":{"$ref":"#/components/schemas/RessourceDslReq"}},"required":["expression","ressource"],"type":"object"},"FenetreApiReq":{"description":"Miroir API d'une fenêtre de maintenance (heures UTC).","properties":{"debut_h":{"description":"Heure de début (0-23, début ≠ fin).","format":"int32","minimum":0,"type":"integer"},"fin_h":{"description":"Heure de fin (traverse minuit si fin < début).","format":"int32","minimum":0,"type":"integer"}},"required":["debut_h","fin_h"],"type":"object"},"FenetreMaintenanceReq":{"properties":{"debut":{"description":"Début de la plage « HH:MM » (inclus).","type":"string"},"fin":{"description":"Fin de la plage « HH:MM » (exclue) — différente du début.","type":"string"}},"required":["debut","fin"],"type":"object"},"FenetreReq":{"properties":{"debut":{"type":"string"},"fin":{"type":"string"}},"required":["debut","fin"],"type":"object"},"FenetreUpgradeApiReq":{"description":"Fenêtre horaire UTC (heures plein, début inclus fin EXCLUE).","properties":{"debut_h":{"format":"int32","minimum":0,"type":"integer"},"fin_h":{"format":"int32","minimum":0,"type":"integer"}},"required":["debut_h","fin_h"],"type":"object"},"FermerReq":{"properties":{"raison":{"description":"Raison obligatoire — fermer, c'est renoncer, pas résoudre.","type":"string"}},"required":["raison"],"type":"object"},"FerroqAcquitterReq":{"properties":{"actions":{"description":"Actions ferroq : [{\"id\", \"action\": \"acquitter|rejeter|prolonger\",\n\"refile\"?, \"extra_ms\"?}] — forme ActionReq de http.rs.","items":{},"type":"array"}},"type":"object"},"FerroqRedriverReq":{"properties":{"limite":{"description":"Nombre max à redriver (0 = tout, défaut 0).","format":"int32","minimum":0,"type":"integer"},"motif":{"description":"Ne redriver que ce motif (null/absent : tout).","type":["string","null"]}},"type":"object"},"FerroqTirerReq":{"properties":{"max":{"description":"Nombre de messages à tirer (1-100, défaut 10 — bornes du broker).","format":"int32","minimum":0,"type":"integer"}},"type":"object"},"FerroqUrlReq":{"properties":{"url":{"description":"Base du broker ferroqd — ex. http://ferroqd.lab:7780","type":"string"}},"required":["url"],"type":"object"},"FichierEdge":{"description":"Un fichier d'artefact versionné : empreinte du contenu entier et\ndécoupe en chunks — la granularité du delta.","properties":{"chemin":{"description":"Chemin relatif propre dans l'artefact (ex. `bin/agent`).","type":"string"},"chunks":{"items":{"$ref":"#/components/schemas/ChunkEdge"},"type":"array"},"empreinte":{"type":"string"},"octets":{"format":"int64","minimum":0,"type":"integer"}},"required":["chemin","empreinte","octets","chunks"],"type":"object"},"FichierEtat":{"description":"Un fichier capturé dans un état de workspace : chemin relatif à la\nracine, taille et empreinte (sha256) MESURÉES — le diff se lit sur\nles empreintes, jamais sur les dates.","properties":{"chemin":{"type":"string"},"empreinte":{"type":"string"},"taille_octets":{"format":"int64","minimum":0,"type":"integer"}},"required":["chemin","taille_octets","empreinte"],"type":"object"},"FichierSnapshotReq":{"properties":{"chemin":{"description":"Chemin relatif à l'export.","type":"string"},"taille_octets":{"description":"Taille MESURÉE au moment du relevé.","format":"int64","minimum":0,"type":"integer"}},"required":["chemin","taille_octets"],"type":"object"},"FiltreEvenement":{"description":"Filtre de correspondance — vide = attrape tout.","properties":{"genres":{"description":"Genres d'événements écoutés (ex. [\"machine.creee\"]). Vide = tous.","items":{"type":"string"},"type":"array"},"titre_contient":{"description":"Le titre doit contenir ce texte (insensible à la casse). Vide = tout.","type":"string"}},"type":"object"},"FiltreRicheApiReq":{"description":"Corps API d'un filtre riche (miroir ToSchema de\n[`crate::regles::FiltreRiche`]) : les conditions arrivent en\nEXPRESSIONS lisibles (« severity >= 3 », « host ~= '^web-' »),\nparsées et validées par le noyau — une expression illisible est\nrefusée nommément.","properties":{"conditions":{"description":"Conditions typées sur attributs imbriqués : chemin a.b.c,\nopérateurs =, !=, >, >=, <, <=, ~= (regex ancrée).","items":{"type":"string"},"type":"array"},"genres":{"description":"Genres d'événements écoutés (vide = tous).","items":{"type":"string"},"type":"array"}},"type":"object"},"FlowObserveeReq":{"description":"Corps d'une flow observée sur l'interface de la machine.","properties":{"direction":{"description":"Sens du paquet observé : ingress | egress.","type":"string"},"distant":{"description":"IPv4 de l'autre bout.","type":"string"},"port_distant":{"format":"int32","minimum":0,"type":["integer","null"]},"port_local":{"format":"int32","minimum":0,"type":["integer","null"]},"protocole":{"description":"tcp | udp | icmp.","type":"string"},"ttl_s":{"description":"TTL des conversations de la table d'état (secondes, défaut 300).","format":"int64","minimum":0,"type":["integer","null"]}},"required":["distant","protocole","direction"],"type":"object"},"FluxGit":{"description":"Un flux git-push → build → déployer vers une stack.","properties":{"branche":{"description":"Branche observée (main, feature/x…).","type":"string"},"depot":{"description":"URL https du dépôt — SANS identifiants incorporés.","type":"string"},"secret_nom":{"description":"NOM du secret qui porte le jeton d'accès — la valeur vit dans\nle coffre, jamais dans ce descripteur.","type":"string"},"stack":{"description":"Stack visée par le déploiement (`stk-…` — doit exister, vérifié\nà l'intégration).","type":"string"}},"required":["stack","branche","depot","secret_nom"],"type":"object"},"FluxMosaiqueReq":{"description":"Flux de la mosaïque : id + époques de ses segments.","properties":{"epochs":{"description":"Époques (secondes) des segments MESURÉS sur le disque (désordonnées acceptées).","items":{"format":"int64","minimum":0,"type":"integer"},"type":"array"},"id":{"description":"Identifiant flx-… du flux.","type":"string"}},"required":["id","epochs"],"type":"object"},"FwRuleReq":{"properties":{"action":{"type":"string"},"commentaire":{"type":"string"},"dport":{"format":"int32","minimum":0,"type":["integer","null"]},"proto":{"type":"string"}},"required":["action","commentaire"],"type":"object"},"GatewayVpnReq":{"description":"Corps API d'une gateway (miroir ToSchema).","properties":{"cle_publique":{"type":"string"},"dernier_handshake":{"description":"Dernier handshake mesuré (epoch, 0 = jamais).","format":"int64","minimum":0,"type":"integer"},"disponible":{"description":"Santé MESURÉE (dérivée du handshake, jamais déclarée).","type":"boolean"},"endpoint":{"type":"string"},"nom":{"type":"string"},"preference":{"description":"0 = préférée, 1 = secours.","format":"int32","minimum":0,"type":"integer"}},"required":["nom","endpoint","cle_publique","preference","disponible","dernier_handshake"],"type":"object"},"GpuDeclare":{"description":"GPU déclaré à l'enregistrement (PVE ne les expose pas via l'API).","properties":{"modele":{"type":"string"},"nombre":{"minimum":0,"type":"integer"}},"required":["modele","nombre"],"type":"object"},"GraphqlReq":{"properties":{"query":{"type":"string"}},"required":["query"],"type":"object"},"GroupeSecuriteReq":{"description":"Corps de pose du security group d'une machine.","properties":{"nom":{"type":"string"},"regles":{"items":{"$ref":"#/components/schemas/RegleSg"},"type":"array"}},"required":["nom","regles"],"type":"object"},"IamCheckReq":{"properties":{"action":{"description":"Action à tester (org.read, project.create…)","type":"string"},"org":{"description":"Organisation où évaluer les bindings","type":"string"},"ressource":{"type":["string","null"]},"sujet":{"description":"Email du sujet évalué","type":"string"}},"required":["sujet","action","org"],"type":"object"},"IamSimulateReq":{"properties":{"action":{"type":"string"},"ressource":{"type":["string","null"]},"role":{"description":"Rôle hypothétique (lecteur, dev, admin-org)","type":"string"},"sujet":{"type":["string","null"]}},"required":["role","action"],"type":"object"},"IdentifiantsCreation":{"description":"Identifiants rendus à la création d'une instance — plus jamais ensuite.","properties":{"mot_de_passe":{"description":"null pour les genres sans couple utilisateur/mot de passe\n(ferroquery : le jeton se crée dans le pod).","type":["string","null"]},"note":{"type":"string"},"uri":{"description":"URI complète (utilisateur + mot de passe) ou connexion brute.","type":"string"},"utilisateur":{"type":"string"}},"required":["utilisateur","uri","note"],"type":"object"},"ImageLotReq":{"properties":{"image_base64":{"type":"string"},"nom_fichier":{"type":"string"}},"required":["nom_fichier","image_base64"],"type":"object"},"ImpersonationStopQuery":{"description":"FIN2-SA-001 — Fin d'impersonation : appelé avec le jeton superadmin\nD'ORIGINE (le client le garde). Serveur minimal HONNÊTE : les JWT\nsont stateless, il n'existe pas de registre de sessions à révoquer —\nle stop JOURNALISE (audit `impersonation.stopped`) et le jeton\nimpersoné expire de lui-même en 30 min maximum. Paramètre `slug`\noptionnel pour tracer de quelle org la session se termine.","properties":{"slug":{"description":"Slug de l'org dont la session d'impersonation se termine (audit).","type":["string","null"]}},"type":"object"},"ImportAnalyseReq":{"properties":{"dump":{"description":"Contenu texte du dump PostgreSQL (pg_dump -s ou complet).","type":"string"}},"required":["dump"],"type":"object"},"ImporterManifesteReq":{"properties":{"llm_id":{"description":"Endpoint LLM de l'org cible (local, jamais porté par le manifeste).","type":"string"},"manifeste":{"description":"Manifeste portable sérialisé (celui que GET /agents/{aid}/manifeste rend)."}},"required":["manifeste","llm_id"],"type":"object"},"IncidentSerde":{"properties":{"composant":{"type":"string"},"numero":{"type":"string"}},"required":["composant","numero"],"type":"object"},"InclinaisonReq":{"properties":{"angle_max":{"format":"double","type":"number"},"pas":{"format":"double","type":"number"},"points":{"description":"Points d'encre [x, y] MESURÉS par le décodeur/moteur en amont.","items":{"items":false,"prefixItems":[{"format":"int32","type":"integer"},{"format":"int32","type":"integer"}],"type":"array"},"type":"array"}},"required":["points"],"type":"object"},"IndexerRagReq":{"properties":{"fichier":{"type":"string"},"taille_max":{"description":"Taille max d'un chunk en caractères (bornes du module).","minimum":0,"type":"integer"},"texte":{"type":"string"}},"required":["fichier","texte"],"type":"object"},"IngererSourcesReq":{"properties":{"evenements":{"items":{"$ref":"#/components/schemas/EvenementSourceReq"},"type":"array"}},"required":["evenements"],"type":"object"},"IngestWhReq":{"properties":{"bucket":{"description":"Bucket S3 du projet à ingérer","type":"string"},"prefixe":{"description":"Préfixe optionnel (dossier virtuel)","type":["string","null"]}},"required":["bucket"],"type":"object"},"IngesterFluxReq":{"properties":{"entite":{"type":"string"},"instant":{"description":"Instant de l'ÉVÉNEMENT (s) — l'écart avec maintenant est le retard mesuré.","format":"int64","minimum":0,"type":"integer"},"maintenant":{"description":"Horloge du serveur si absent — mesuré, jamais déduit.","format":"int64","minimum":0,"type":["integer","null"]},"source":{"type":"string"},"valeur":{"format":"double","type":"number"}},"required":["entite","source","instant","valeur"],"type":"object"},"IngesterLogsReq":{"properties":{"lignes":{"items":{"$ref":"#/components/schemas/LigneLogReq"},"type":"array"}},"required":["lignes"],"type":"object"},"IngesterVersionReq":{"properties":{"entite":{"type":"string"},"valeurs":{"additionalProperties":{"$ref":"#/components/schemas/ValeurFeature"},"description":"Toutes les features déclarées présentes, aucune inconnue.","propertyNames":{"type":"string"},"type":"object"}},"required":["entite","valeurs"],"type":"object"},"IngestionReq":{"properties":{"entite":{"type":"string"},"valeurs":{"additionalProperties":{"$ref":"#/components/schemas/ValeurFeature"},"propertyNames":{"type":"string"},"type":"object"}},"required":["entite","valeurs"],"type":"object"},"InitVpnReq":{"properties":{"endpoint":{"description":"Endpoint publique du serveur (ex. 192.168.1.110:51820).","type":"string"}},"required":["endpoint"],"type":"object"},"InscriptionReq":{"properties":{"email":{"type":"string"},"nom":{"type":"string"},"nom_organisation":{"type":"string"},"prenom":{"type":"string"},"tel":{"type":"string"}},"required":["nom","prenom","email","tel","nom_organisation"],"type":"object"},"InscrireUtilisateurAppReq":{"properties":{"email":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["email","roles"],"type":"object"},"InstallMarketplaceReq":{"properties":{"app":{"type":"string"},"org":{"type":"string"}},"required":["org","app"],"type":"object"},"InstanceCree":{"description":"POST /v1/projects/{pid}/instances — instance provisionnée. Le code\nhistorique réellement servi est 200 (pas 201 — B5, audit 16/09).","properties":{"equivalents":{"description":"Équivalents CLI/OpenTofu de la création."},"identifiants":{"$ref":"#/components/schemas/IdentifiantsCreation","description":"Identifiants honnêtes rendus une fois : URI complète\n(utilisateur + mot de passe) ; `admin_password` reste en alias\ndéprécié sur l'objet instance."},"instance":{"$ref":"#/components/schemas/InstanceReelle","description":"Instance complète, admin_password compris (rendu UNE seule fois\nici ; VIDE pour ferroquery : son serveur n'accepte ni --token ni\nenv — le jeton se crée dans le pod, voir `connexion`)."},"pret":{"description":"False pendant le démarrage des nœuds (true quand tout est lancé).","type":"boolean"}},"required":["instance","identifiants","pret","equivalents"],"type":"object"},"InstancePublique":{"description":"Instance telle que servie en liste/détail : la forme publique de\n`InstanceReelle`, `admin_password` retiré (M4, audit 14/09).","properties":{"backups":{"description":"Sauvegardes (P8-02) : horodatage + chemin.","items":{"$ref":"#/components/schemas/BackupInstance"},"type":"array"},"connexion":{"description":"Connexion principale (pgwire pour ferrodb, HTTP pour ferroquery).","type":"string"},"cree_le":{"type":"string"},"dossier":{"type":"string"},"etat":{"description":"demarrage | demarree | plantee | arrete | supprime","type":"string"},"genre":{"description":"ferrodb | ferroquery | seaweedfs | opensearch | ferroq | postgres","type":"string"},"id":{"type":"string"},"noeuds":{"minimum":0,"type":"integer"},"noeuds_detail":{"description":"Connexions par nœud (pgwire/grpc/metrics).","items":{"$ref":"#/components/schemas/NoeudInstance"},"type":"array"},"nom":{"type":"string"},"org":{"type":"string"}},"required":["id","nom","genre","noeuds","org","etat","cree_le","dossier","connexion","noeuds_detail","backups"],"type":"object"},"InstanceReelle":{"properties":{"admin_password":{"description":"Mot de passe admin (ferrodb : phrase KMS + admin SQL ; postgres :\nsuperuser). VIDE pour ferroquery, tous chemins : son serveur ne\nlit NI --token NI env d'auth — les jetons API se créent dans le\npod (`ferroquery admin add-user`, anomalie 2 du 15/09 : la\nvaleur livrée ici n'était jamais câblée, plus de promesse creuse).","type":"string"},"backups":{"description":"Sauvegardes (P8-02) : horodatage + chemin.","items":{"$ref":"#/components/schemas/BackupInstance"},"type":"array"},"connexion":{"description":"Connexion principale (pgwire pour ferrodb, HTTP pour ferroquery).","type":"string"},"cree_le":{"type":"string"},"dossier":{"type":"string"},"etat":{"description":"\"demarrage\" | \"demarree\" | \"plantee\" | \"arrete\"","type":"string"},"genre":{"description":"\"ferrodb\" | \"ferroquery\" | \"seaweedfs\" | \"opensearch\" | \"ferroq\"","type":"string"},"id":{"type":"string"},"noeuds":{"minimum":0,"type":"integer"},"noeuds_detail":{"description":"Connexions par nœud (pgwire/grpc/metrics).","items":{"$ref":"#/components/schemas/NoeudInstance"},"type":"array"},"nom":{"type":"string"},"org":{"type":"string"}},"required":["id","nom","genre","noeuds","org","etat","cree_le","dossier","connexion"],"type":"object"},"InstanceSuperadminVue":{"description":"Instance (FerroDB/FerroQuery/…) dans la vue superadmin.","properties":{"connexion":{"type":"string"},"etat":{"type":"string"},"genre":{"description":"ferrodb | ferroquery | seaweedfs | opensearch | ferroq | postgres","type":"string"},"noeuds":{"minimum":0,"type":"integer"},"nom":{"type":"string"}},"required":["nom","genre","noeuds","connexion","etat"],"type":"object"},"InstancesListe":{"description":"GET /v1/projects/{pid}/instances — instances de l'org, SANS\nadmin_password (le mot de passe n'est rendu qu'UNE SEULE FOIS, à la\ncréation).","properties":{"items":{"items":{"$ref":"#/components/schemas/InstancePublique"},"type":"array"},"total":{"minimum":0,"type":"integer"}},"required":["items","total"],"type":"object"},"InstancierUcReq":{"additionalProperties":false,"description":"--- CONFORMITE INCOHERENCES (E1, fragments g03-g10) ---\nCorps de `POST /usecases/{slug}/instancier` : le corps était ignoré\n(aucun extracteur) — `{\"prefixe\":\"x-\"}` n'avait AUCUN effet sur les\nnoms créés. Désormais le préfixe s'applique aux noms des ressources\ndu plan (instances, topics), borné : 1 à 16 caractères\nalphanumériques ou tirets. Le corps est lu en octets bruts : VIDE\n(même avec Content-Type: application/json) = aucune option, comme\navant — un corps non vide non supporté est refusé proprement 400,\njamais ignoré silencieusement.","properties":{"prefixe":{"description":"Préfixe des noms de ressources créées (ex. « x- » → instance\n« x-uc-cicd-pg »). 1-16 caractères alphanumériques ou tirets.\nCorps absent ou vide : noms du plan, comme avant.","type":["string","null"]}},"type":"object"},"InvocationConteneurApiReq":{"description":"Miroir API d'une invocation conteneur MESURÉE (le noyau est sans ToSchema).","properties":{"duree_ms":{"description":"Durée réelle du traitement (ms).","format":"int64","minimum":0,"type":"integer"},"erreur":{"description":"L'appel a échoué (erreur applicative ou conteneur mort).","type":"boolean"},"froid":{"description":"Démarrage à froid subi (pas d'instance chaude disponible).","type":"boolean"},"memoire_mo":{"description":"Pic mémoire lu pendant l'exécution (Mo).","format":"int64","minimum":0,"type":"integer"}},"required":["duree_ms","memoire_mo","erreur","froid"],"type":"object"},"InvoquerWarmReq":{"description":"Invocation sur un pool — l'horloge vient de l'appelant.","properties":{"maintenant":{"description":"Secondes epoch — fournie par l'appelant (le noyau ne lit pas le temps).","format":"int64","minimum":0,"type":"integer"}},"required":["maintenant"],"type":"object"},"Ipv6SuperadminVue":{"description":"Allocation IPv6 dans la vue superadmin.","properties":{"cible":{"description":"Cible libre : vmid Proxmox, nom NAT…","type":"string"},"cible_type":{"type":"string"},"ip":{"type":"string"}},"required":["ip","cible","cible_type"],"type":"object"},"IsolationForestReq":{"properties":{"graine":{"description":"Graine du PRNG xorshift64 — DÉTERMINISTE : mêmes points + même\ngraine ⇒ mêmes scores. Défaut 42 (DIT).","format":"int64","minimum":0,"type":["integer","null"]},"n_arbres":{"description":"Arbres de la forêt — défaut 100 (DIT).","minimum":0,"type":["integer","null"]},"points":{"description":"Points à scorer (mêmes dimensions pour tous).","items":{"items":{"format":"double","type":"number"},"type":"array"},"type":"array"},"seuil":{"description":"Seuil des anomalies dans l'échelle des scores 1..=2 — défaut 1.5.","format":"double","type":["number","null"]},"sous_echantillon":{"description":"Sous-échantillon par arbre (ψ) — défaut 256, classique iForest.","minimum":0,"type":["integer","null"]}},"required":["points"],"type":"object"},"JetonApp":{"description":"Un jeton d'app : sa charge, signée Ed25519 (hex) — vérifiable hors\nligne par la clé publique de l'app.","properties":{"charge":{"$ref":"#/components/schemas/ChargeJetonApp"},"signature":{"description":"Signature Ed25519 du JSON canonique de la charge, hex.","type":"string"}},"required":["charge","signature"],"type":"object"},"JobConteneurReq":{"description":"Corps d'attache d'un descripteur conteneurisé à un job.","properties":{"args":{"description":"argv du conteneur (le premier élément est la commande).","items":{"type":"string"},"type":"array"},"image":{"description":"Image conteneur ÉTIQUETÉE (ex. `ghcr.io/exemple/etl:1.4`).","type":"string"},"job_id":{"description":"Id du job EXISTANT du lot.","type":"string"},"memoire_max_mo":{"description":"Limite mémoire (Mo) — bornes des fonctions serverless.","format":"int64","minimum":0,"type":"integer"},"timeout_s":{"description":"Limite de durée (secondes) — bornes des jobs du lot.","format":"int64","minimum":0,"type":"integer"}},"required":["job_id","image","args","memoire_max_mo","timeout_s"],"type":"object"},"JugerRejeuReq":{"properties":{"echantillon":{"description":"L'échantillon RENDU par /rejeu/echantillonner.","items":{"$ref":"#/components/schemas/RequeteHistReq"},"type":"array"},"reponses":{"description":"Les réponses MESURÉES de la version candidate (même id, même question).","items":{"$ref":"#/components/schemas/ReponseRejeuReq"},"type":"array"}},"required":["echantillon","reponses"],"type":"object"},"K8sPlacementReq":{"properties":{"cluster":{"description":"Nom ou id du cluster k3s RÉEL de l'org.","type":"string"},"taille_pvc_gb":{"description":"Taille (Gi) du PVC de données des genres persistants\n(ferrodb/postgres/ferroquery) — défaut 8 Gi, bornes 1..=1000.\nIgnoré pour les genres sans PVC.","format":"int32","minimum":0,"type":["integer","null"]}},"required":["cluster"],"type":"object"},"LectureFuseReq":{"description":"Une lecture à rejouer sur l'archive montée.","properties":{"demande":{"description":"Octets demandés par la lecture.","format":"int64","minimum":0,"type":"integer"},"offset":{"description":"Décalage en octets dans l'archive.","format":"int64","minimum":0,"type":"integer"}},"required":["offset","demande"],"type":"object"},"LiaisonMesh":{"description":"Une liaison entre deux sites (non orientée).","properties":{"a":{"type":"string"},"b":{"type":"string"}},"required":["a","b"],"type":"object"},"LiaisonSsoGitea":{"description":"Liaison SSO d'une org vers sa forge Gitea : l'org devient\nfournisseur OAuth2 de la forge — ses membres se connectent à Gitea\navec leur session org. Le SECRET client ne voyage JAMAIS dans le\ndescripteur : seul le NOM du secret cirroSecrets qui le porte est\nréférencé (rotation et ACL gérées par les secrets, pas par la forge).","properties":{"admin_groupes":{"description":"Groupes org dont les membres deviennent administrateurs de la forge.","items":{"type":"string"},"type":"array"},"client_id":{"description":"client_id OAuth2 déclaré dans Gitea (public par nature).","type":"string"},"creation_auto":{"description":"Créer les membres dans Gitea à leur première connexion.","type":"boolean"},"etat":{"description":"\"brouillon\" | \"active\" | \"rompue\" (rompue = échange réel échoué,\nmesuré par l'échec des appels, pas déduit de l'âge).","type":"string"},"org":{"type":"string"},"ref_secret_client":{"description":"NOM du secret cirroSecrets portant le client_secret — jamais la valeur.","type":"string"},"url_gitea":{"description":"URL de la forge (celle du plan de déploiement).","type":"string"}},"required":["org","url_gitea","client_id","ref_secret_client","creation_auto","admin_groupes","etat"],"type":"object"},"LibelleEtat":{"description":"Légende des libellés canoniques (rendue par GET /v1/etats).","properties":{"description":{"type":"string"},"libelle":{"type":"string"}},"required":["libelle","description"],"type":"object"},"LienCurate":{"description":"Lien curaté (P6-01) — lu depuis la metadata `portail:{slug}:liens`.","properties":{"epingle":{"type":"boolean"},"groupe":{"description":"Groupe d'affichage (« Liens » par défaut).","type":["string","null"]},"nom":{"type":"string"},"url":{"description":"Doit commencer par http:// ou https://.","type":"string"}},"required":["nom","url"],"type":"object"},"LiensReq":{"description":"Corps attendu du PUT /v1/projects/{pid}/portail/liens.","properties":{"liens":{"items":{"$ref":"#/components/schemas/LienCurate"},"type":"array"}},"required":["liens"],"type":"object"},"LigneCoutRessource":{"description":"Une ligne = une ressource vivante et son coût mensuel dérivé des\ntarifs existants (jamais inventé : sans tarif → pas de ligne).","properties":{"cout_mois":{"description":"€/mois (tarif horaire × 730, arrondi au centime).","format":"double","type":"number"},"detail":{"description":"Ex. « 4 vCPU · 8 Go · 100 Go disque » — None si rien à dire.","type":["string","null"]},"genre":{"description":"machine | vm | instance | volume | ip | serveur","type":"string"},"nom":{"type":"string"}},"required":["genre","nom","cout_mois"],"type":"object"},"LigneLogReq":{"properties":{"composant":{"type":"string"},"corps":{"type":"string"},"niveau":{"type":"string"},"trace_id":{"type":["string","null"]}},"required":["niveau","composant","corps"],"type":"object"},"LigneSimulReq":{"properties":{"famille":{"type":"string"},"gabarit":{"description":"Gabarit cs-* du catalogue — inconnu ⇒ refus cité.","type":"string"},"quantite":{"format":"int32","minimum":0,"type":"integer"}},"required":["famille","gabarit","quantite"],"type":"object"},"LimitationWafReq":{"description":"La demande de pose des règles de limitation (remplace la liste).","properties":{"regles":{"description":"Règles complètes (chacune validée par le module bouclier).","items":{"$ref":"#/components/schemas/RegleLimitationReq"},"type":"array"}},"required":["regles"],"type":"object"},"LireReq":{"properties":{"depuis":{"description":"Curseur : lire les enregistrements de séquence STRICTEMENT\nsupérieure. 0 = depuis le début (retenus).","format":"int64","minimum":0,"type":"integer"},"max":{"minimum":0,"type":"integer"}},"type":"object"},"ListenerReq":{"properties":{"couche":{"description":"l7 (proxy HTTP) | l4 (relais d'octets).","type":"string"},"port":{"format":"int32","minimum":0,"type":"integer"},"protocole":{"description":"http pour l7 ; tcp | udp pour l4.","type":"string"},"repartition":{"description":"round-robin | source-ip (l4 uniquement).","type":"string"}},"required":["couche","protocole","port","repartition"],"type":"object"},"LoginPortailReq":{"description":"Corps du login portail.","properties":{"email":{"type":"string"},"password":{"type":"string"}},"required":["email","password"],"type":"object"},"LoginReq":{"properties":{"code":{"type":["string","null"]},"email":{"type":"string"},"password":{"type":"string"}},"required":["email","password"],"type":"object"},"LotProvisionReq":{"properties":{"genre":{"description":"Genre des things créés (ex. capteur).","type":"string"},"graine":{"description":"Graine du provisioning déterministe (≥ 8 caractères).","type":"string"},"n":{"description":"Nombre d'identités (1-100, cap things d'une org).","minimum":0,"type":"integer"},"prefixe":{"description":"Préfixe des identités (iot-{prefixe}-001…).","type":"string"}},"required":["prefixe","n","graine"],"type":"object"},"LotVisionReq":{"properties":{"images":{"items":{"$ref":"#/components/schemas/ImageLotReq"},"type":"array"},"page":{"description":"Page demandée (1-based, défaut 1).","minimum":0,"type":"integer"},"taille_page":{"minimum":0,"type":"integer"}},"required":["images"],"type":"object"},"MachineActionReq":{"properties":{"action":{"description":"\"demarrer\" | \"arreter\" | \"redemarrer\"","type":"string"}},"required":["action"],"type":"object"},"MachineSuperadminVue":{"description":"Machine Proxmox réelle dans la vue superadmin.","properties":{"disque_gb":{"format":"int64","type":"integer"},"etat":{"type":"string"},"genre":{"description":"vm | lxc","type":"string"},"nom":{"type":"string"},"ram_mb":{"format":"int64","type":"integer"},"vcpus":{"format":"int64","type":"integer"},"vmid":{"format":"int64","type":"integer"}},"required":["nom","genre","vmid","vcpus","ram_mb","disque_gb","etat"],"type":"object"},"MaintenanceReq":{"description":"La demande de maintenance planifiée.","properties":{"debut":{"description":"RFC 3339 du début (inclus).","type":"string"},"detail":{"description":"Impact, périmètre (≤ 500 caractères).","type":"string"},"fin":{"description":"RFC 3339 de la fin (exclue).","type":"string"},"titre":{"type":"string"}},"required":["titre","debut","fin"],"type":"object"},"MajConsommateurReq":{"properties":{"actif":{"type":["boolean","null"]},"nom":{"type":["string","null"]},"quota_horaire":{"format":"int32","minimum":0,"type":["integer","null"]}},"type":"object"},"MajDataflowReq":{"properties":{"actif":{"type":["boolean","null"]},"planifie":{"type":["string","null"]},"source_sql":{"type":["string","null"]},"transformation_sql":{"type":["string","null"]}},"type":"object"},"MajDigestsReq":{"properties":{"digest_cible":{"type":["string","null"]},"digest_source":{"type":["string","null"]}},"type":"object"},"MajMachineReq":{"properties":{"disque_gb":{"description":"Nouvelle taille disque en Go — CROISSANCE seule (Proxmox ne rétrécit pas)","format":"int64","type":["integer","null"]},"ram_mb":{"description":"Nouvelle RAM en Mo (512-262144)","format":"int64","type":["integer","null"]},"vcpus":{"description":"Nouveau nombre de vCPU (1-64)","format":"int64","type":["integer","null"]}},"type":"object"},"MajMarketplaceReq":{"properties":{"version":{"description":"Version EXACTE visée (semver X.Y.Z — jamais latest).","type":"string"}},"required":["version"],"type":"object"},"MajPlanReq":{"properties":{"actif":{"type":["boolean","null"]},"frequence":{"type":["string","null"]},"heure_utc":{"format":"int32","minimum":0,"type":["integer","null"]},"retention":{"format":"int32","minimum":0,"type":["integer","null"]},"stockage_replique":{"description":"Nouveau stockage secondaire — la chaîne VIDE retire la\nréplication (PUT remplace le champ quand il est présent).","type":["string","null"]}},"type":"object"},"MajPontMatrixReq":{"properties":{"actif":{"type":["boolean","null"]},"politique":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PolitiqueReponse"}]}},"type":"object"},"MajReglagesReq":{"properties":{"admission_mode":{"description":"audit (journalise) ou enforce (rejette)","type":"string"},"registry_endpoint":{"type":["string","null"]}},"required":["admission_mode"],"type":"object"},"MajReseauReq":{"properties":{"dhcp":{"description":"Bascule du serveur DHCP (exige une plage pour activer).","type":"boolean"},"plage_dhcp":{"items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":["array","null"]}},"required":["dhcp"],"type":"object"},"MajRotationReq":{"properties":{"jours":{"description":"0 désactive la rotation, 1-90 jours l'active.","format":"int32","minimum":0,"type":"integer"}},"required":["jours"],"type":"object"},"MajShadowReq":{"properties":{"state":{"description":"Forme AWS : {\"state\": {\"desired\": {…}}} — fusion par clés."}},"required":["state"],"type":"object"},"MajUtilisateurAppReq":{"properties":{"action":{"description":"role_ajouter | role_retirer | desactiver | reactiver.","type":"string"},"role":{"type":["string","null"]}},"required":["action"],"type":"object"},"MajWebhookDeliveryReq":{"properties":{"actif":{"type":["boolean","null"]},"genres":{"items":{"type":"string"},"type":["array","null"]}},"type":"object"},"ManifesteAutopilotApiReq":{"description":"Miroir API du manifeste autopilot (pods + bornes, AUCUN nœud).","properties":{"max_noeuds":{"description":"Borne haute du pool implicite (nœuds).","format":"int32","minimum":0,"type":"integer"},"min_noeuds":{"description":"Borne basse du pool implicite (nœuds).","format":"int32","minimum":0,"type":"integer"},"pods":{"items":{"$ref":"#/components/schemas/PodAutopilotApiReq"},"type":"array"}},"required":["pods","min_noeuds","max_noeuds"],"type":"object"},"ManifesteSauvegardeReq":{"description":"Corps de dépôt d'un manifeste de sauvegarde.","properties":{"backup_id":{"type":"string"},"date":{"description":"RFC 3339 de la sauvegarde — date les occurrences.","type":"string"},"fichiers":{"description":"Chemins des fichiers couverts (ex. etc/nginx/nginx.conf).","items":{"type":"string"},"type":"array"},"machine":{"type":"string"}},"required":["backup_id","machine","date","fichiers"],"type":"object"},"MapperClaimsReq":{"properties":{"claims":{"additionalProperties":{"type":"string"},"propertyNames":{"type":"string"},"type":"object"}},"required":["claims"],"type":"object"},"MappingDns":{"description":"Un FQDN à piloter : dans la zone `zone` de l'org `org`, le record A\n`nom` doit valoir `primaire` quand le site actif est « primaire »,\n`secondaire` sinon.","properties":{"nom":{"description":"Nom relatif dans la zone (« @ », « www »…).","type":"string"},"org":{"type":"string"},"primaire":{"type":"string"},"secondaire":{"type":"string"},"ttl":{"format":"int32","minimum":0,"type":"integer"},"zone":{"description":"Nom de la zone (ex. « lab.cirrostratus.internal »).","type":"string"}},"required":["org","zone","nom","primaire","secondaire"],"type":"object"},"Member":{"properties":{"created_at":{"type":"string"},"email":{"type":"string"},"id":{"type":"string"},"org_id":{"type":"string"},"role":{"type":"string"},"user_id":{"type":"string"}},"required":["id","org_id","user_id","email","role","created_at"],"type":"object"},"MembreDetailVue":{"description":"Membre d'une org dans la vue superadmin.","properties":{"email":{"type":"string"},"role":{"description":"admin-org | dev | lecteur","type":"string"}},"required":["email","role"],"type":"object"},"MenageReq":{"properties":{"appliquer_confirmees_seulement":{"description":"Règle produit : seul un plan d'application des recos CONFIRMÉES\nest accepté.","type":"boolean"},"periodicit_jours":{"description":"Jours entre deux passages (refusé si 0).","format":"int32","minimum":0,"type":"integer"},"recos":{"description":"Recos (celles de GET …/recos/etendues, confirmées ou non).","items":{"$ref":"#/components/schemas/RecoMenageReq"},"type":"array"}},"required":["recos","periodicit_jours","appliquer_confirmees_seulement"],"type":"object"},"MeshVpnReq":{"description":"Corps de génération (miroir ToSchema).","properties":{"sites":{"items":{"$ref":"#/components/schemas/SiteMeshVpnReq"},"type":"array"}},"required":["sites"],"type":"object"},"MessageChat":{"properties":{"content":{"type":"string"},"role":{"type":"string"}},"required":["role","content"],"type":"object"},"MesureCibleDnsReq":{"description":"Corps d'une mesure de sonde (miroir ToSchema).","properties":{"cible":{"type":"string"},"disponible":{"description":"Santé mesurée par la sonde de l'appelant.","type":"boolean"},"rtt_ms":{"description":"RTT mesuré (ms) — None = jamais mesuré.","format":"int32","minimum":0,"type":["integer","null"]}},"required":["cible","disponible"],"type":"object"},"MesuresCanari":{"description":"Mesures d'UNE révision pendant le canari : compteurs MESURÉS par\nle proxy (même contrat que [`MetriquesApp`]).","properties":{"erreurs":{"format":"int64","minimum":0,"type":"integer"},"requetes":{"format":"int64","minimum":0,"type":"integer"}},"required":["requetes","erreurs"],"type":"object"},"MesuresCanariReq":{"description":"Corps d'apport des mesures du canari (compteurs MESURÉS du proxy).","properties":{"canari":{"$ref":"#/components/schemas/MesuresCanari"},"stable":{"$ref":"#/components/schemas/MesuresCanari"}},"required":["stable","canari"],"type":"object"},"MetriquePerso":{"description":"Déclaration d'une métrique personnalisée de scaling.","properties":{"fenetre":{"description":"Nombre d'échantillons MESURÉS constituant la fenêtre de\ndécision (la moyenne porte sur les `fenetre` derniers).","minimum":0,"type":"integer"},"nom":{"description":"Nom lisible (ex. \"file-rabbitmq\", \"rps-api\").","type":"string"},"seuil_bas":{"description":"Valeur moyenne en-deçà de laquelle on retire une VM.","format":"double","type":"number"},"seuil_haut":{"description":"Valeur moyenne au-delà de laquelle on ajoute une VM.","format":"double","type":"number"}},"required":["nom","seuil_haut","seuil_bas","fenetre"],"type":"object"},"MetriquesCacheDto":{"description":"Alller-retour KV des métriques INFO du moteur.","properties":{"commandes_s":{"format":"int64","minimum":0,"type":"integer"},"connexions":{"format":"int64","minimum":0,"type":"integer"},"evictions":{"format":"int64","minimum":0,"type":"integer"},"hits":{"format":"int64","minimum":0,"type":"integer"},"misses":{"format":"int64","minimum":0,"type":"integer"},"octets_utilises":{"format":"int64","minimum":0,"type":"integer"}},"required":["octets_utilises","connexions","hits","misses","evictions","commandes_s"],"type":"object"},"MfaEnableReq":{"properties":{"code":{"type":"string"}},"required":["code"],"type":"object"},"MicroLotsReq":{"description":"Corps de pose d'une planification en micro-lots.","properties":{"intervalle_s":{"format":"int64","minimum":0,"type":"integer"}},"required":["intervalle_s"],"type":"object"},"MigrationReq":{"properties":{"tf":{"description":"Contenu du fichier `.tf` à analyser.","type":"string"}},"required":["tf"],"type":"object"},"MoniteurService":{"description":"Un moniteur de service.","properties":{"criticite":{"description":"1 (vital) - 5 (confort).","format":"int32","minimum":0,"type":"integer"},"echecs_consecutifs":{"description":"Échecs consécutifs mesurés à l'instant jugé.","format":"int32","minimum":0,"type":"integer"},"service":{"description":"Nom du service surveillé (libre).","type":"string"},"seuil_echecs":{"description":"Échecs consécutifs MESURÉS avant de réclamer la bascule.","format":"int32","minimum":0,"type":"integer"}},"required":["service","criticite","seuil_echecs","echecs_consecutifs"],"type":"object"},"MonterVolumeReq":{"properties":{"volume_id":{"type":"string"}},"required":["volume_id"],"type":"object"},"MosaiqueReq":{"description":"Corps d'une composition de mosaïque.","properties":{"colonnes":{"description":"Colonnes de la grille (1-4, ≤ nombre de flux).","minimum":0,"type":"integer"},"flux":{"description":"Flux à composer (2 minimum).","items":{"$ref":"#/components/schemas/FluxMosaiqueReq"},"type":"array"},"hauteur_cible":{"format":"int32","minimum":0,"type":"integer"},"largeur_cible":{"format":"int32","minimum":0,"type":"integer"}},"required":["flux","colonnes","largeur_cible","hauteur_cible"],"type":"object"},"MotPdfReq":{"description":"Un mot de PDF aux coordonnées mesurées (miroir API de vision::MotPdf).","properties":{"largeur":{"format":"double","type":"number"},"page":{"description":"Page 1-based.","format":"int32","minimum":0,"type":"integer"},"texte":{"type":"string"},"x":{"description":"Bord gauche du mot (points PDF).","format":"double","type":"number"},"y":{"description":"Ligne de base du mot (points PDF).","format":"double","type":"number"}},"required":["page","x","y","largeur","texte"],"type":"object"},"MultipartReq":{"description":"Corps d'un upload multipart.","properties":{"cle":{"description":"Clé de l'objet.","type":"string"},"parties":{"items":{"$ref":"#/components/schemas/PartieUploadReq"},"type":"array"}},"required":["cle","parties"],"type":"object"},"MutationGraphqlReq":{"description":"Corps d'une mutation GraphQL.","properties":{"query":{"type":"string"}},"required":["query"],"type":"object"},"NerReq":{"properties":{"texte":{"description":"Texte à analyser (1 à 512 Kio caractères).","type":"string"}},"required":["texte"],"type":"object"},"NewAbonnementReq":{"properties":{"destination":{"type":"string"},"filtre":{"type":"string"},"protocole":{"description":"\"webhook\" ou \"ferroq\".","type":"string"}},"required":["protocole","destination"],"type":"object"},"NewAgentReq":{"properties":{"description":{"type":"string"},"instructions":{"type":"string"},"llm_id":{"type":"string"},"memoire":{"type":"boolean"},"nom":{"type":"string"},"outils":{"items":{"type":"string"},"type":"array"},"politique":{"$ref":"#/components/schemas/Politique"}},"required":["nom","instructions","llm_id"],"type":"object"},"NewAliasReq":{"properties":{"cibles":{"description":"IDs d'endpoints, ordre = préférence de routage","items":{"type":"string"},"type":"array"},"nom":{"type":"string"}},"required":["nom","cibles"],"type":"object"},"NewAnalyseReq":{"description":"Demande d'analyse : nom + corps binaire en base64 (même transport\nque la transcription — cirroTranscription 0.83).","properties":{"image_base64":{"description":"Contenu binaire de l'image encodé base64 (≤ 20 Mo décodé).","type":"string"},"nom_fichier":{"description":"≤ 120 caractères, sans « / ».","type":"string"}},"required":["nom_fichier","image_base64"],"type":"object"},"NewApiKeyReq":{"properties":{"expires_days":{"format":"int64","type":["integer","null"]},"name":{"type":"string"}},"required":["name"],"type":"object"},"NewAppReq":{"properties":{"conge_auto":{"type":"boolean"},"image":{"type":"string"},"max_instances":{"format":"int32","minimum":0,"type":"integer"},"min_instances":{"format":"int32","minimum":0,"type":"integer"},"nom":{"type":"string"},"port":{"format":"int32","minimum":0,"type":"integer"}},"required":["nom","image"],"type":"object"},"NewArchiveReq":{"properties":{"nom":{"type":"string"},"retention_jours":{"description":"Rétention en jours (absent/None = indéfiniment).","format":"int32","minimum":0,"type":["integer","null"]},"source":{"description":"Chemin absolu du dossier à archiver (doit exister).","type":"string"}},"required":["nom","source"],"type":"object"},"NewBanReq":{"properties":{"ip":{"type":"string"},"motif":{"type":"string"}},"required":["ip"],"type":"object"},"NewBucketReq":{"properties":{"nom":{"type":"string"},"taille_limite_gb":{"format":"int64","type":"integer"}},"required":["nom"],"type":"object"},"NewBudgetPeriodeReq":{"properties":{"debut":{"description":"Début inclus « AAAA-MM-DD ».","type":"string"},"fin":{"description":"Fin incluse « AAAA-MM-DD ».","type":"string"},"limite":{"description":"Plafond propre à la plage (€).","format":"double","type":"number"}},"required":["debut","fin","limite"],"type":"object"},"NewBudgetReq":{"properties":{"limite_mois":{"format":"double","type":"number"},"nom":{"type":"string"}},"required":["nom","limite_mois"],"type":"object"},"NewCacheReq":{"properties":{"mode_eviction":{"description":"allkeys-lru | volatile-lru | noeviction.","type":"string"},"nom":{"type":"string"},"plan":{"description":"cache-nano | cache-small | cache-medium.","type":"string"},"ttl_defaut_s":{"description":"TTL par défaut des clés sans expiration (secondes).","format":"int32","minimum":0,"type":"integer"}},"required":["nom","plan","mode_eviction","ttl_defaut_s"],"type":"object"},"NewCampagneReq":{"properties":{"corps":{"type":"string"},"nom":{"type":"string"},"segment":{"description":"Segment ciblé (seg-…).","type":"string"},"sujet":{"type":"string"}},"required":["nom","sujet","corps","segment"],"type":"object"},"NewCertReq":{"properties":{"cn":{"description":"Nom commun (ex. www.cirro.lab).","type":"string"},"sans":{"description":"SAN DNS additionnels.","items":{"type":"string"},"type":"array"}},"required":["cn"],"type":"object"},"NewCibleReq":{"properties":{"adresse":{"type":"string"}},"required":["adresse"],"type":"object"},"NewCleS3Req":{"description":"Corps de POST /s3/cles.","properties":{"droits":{"description":"Droits accordés (au moins un).","items":{"$ref":"#/components/schemas/DroitS3"},"type":"array"},"nom":{"description":"Nom lisible (label) — ex. « ci-pipeline ».","type":"string"}},"required":["nom","droits"],"type":"object"},"NewComposantReq":{"properties":{"artefact":{"type":"string"},"nom":{"type":"string"}},"required":["nom","artefact"],"type":"object"},"NewConsommateurReq":{"properties":{"nom":{"type":"string"},"quota_horaire":{"format":"int32","minimum":0,"type":"integer"}},"required":["nom"],"type":"object"},"NewCourrierEtenduReq":{"description":"Requête d'envoi ÉTENDUE (FLUX W-12) : les champs du message + des\npièces jointes en base64 (le module `NewMessageReq` reste tel quel,\nla structure étendue vit côté routeur — même validation, bornes\nexplicites du module).","properties":{"a":{"description":"Destinataires (1-20).","items":{"type":"string"},"type":"array"},"corps":{"type":"string"},"de":{"description":"Expéditeur (doit être accepté par le relais).","type":"string"},"pieces_jointes":{"description":"Pièces jointes (base64 standard, ≤ 5 pièces, 5 Mo chacune,\n10 Mo cumulés — validées AVANT toute tentative réseau).","items":{"$ref":"#/components/schemas/PieceJointeReq"},"type":"array"},"sujet":{"type":"string"}},"required":["sujet","de","a","corps"],"type":"object"},"NewCreneauReq":{"properties":{"debut":{"type":"string"},"fin":{"type":"string"},"priorite":{"format":"int64","type":"integer"}},"required":["debut","fin"],"type":"object"},"NewCronReq":{"properties":{"commande":{"description":"Commande à exécuter à chaque déclenchement. Si posée, UNE cible\nest obligatoire : `serveur_id` (SSH) ou `machine_id` (pct exec).\nVide/absent = pur déclencheur (audit + webhooks, rien n'est exécuté).","type":["string","null"]},"expression":{"type":"string"},"machine_id":{"description":"Cible : machine PVE (POST /machines) — exécution pct exec","type":["string","null"]},"nom":{"type":"string"},"serveur_id":{"description":"Cible : serveur externe enregistré (POST /serveurs) — exécution SSH","type":["string","null"]}},"required":["nom","expression"],"type":"object"},"NewDatabaseReq":{"properties":{"moteur":{"description":"\"ferrodb\" (défaut) | \"postgres\" — instance PostgreSQL réelle (0.87).","type":["string","null"]},"nom":{"type":"string"},"plan":{"type":"string"}},"required":["nom"],"type":"object"},"NewDataflowReq":{"description":"Corps de POST /dataflows — Mi2 (audit 14/09) : ce struct s'appelait\n`NewPipelineReq` comme le corps CI/CD de `crate::pipelines` (source\nimage/git) ; les deux schémas utoipa entraient en collision sous le\nMÊME nom de composant et l'openapi documentait `source` (image/git)\npour la route dataflows alors que le handler exige `source_sql`.\nRenommé pour lever la collision — le contrat JSON est inchangé.","properties":{"nom":{"type":"string"},"planifie":{"type":["string","null"]},"source_sql":{"type":"string"},"transformation_sql":{"type":["string","null"]}},"required":["nom","source_sql"],"type":"object"},"NewDatasetReq":{"properties":{"nom":{"description":"Nom du dataset (mêmes règles que les modèles : 1-40, alphanum\n+ `- _ .`).","type":"string"}},"required":["nom"],"type":"object"},"NewDemandeQuotaReq":{"properties":{"champ":{"type":"string"},"justification":{"type":"string"},"valeur_demandee":{"format":"int64","type":"integer"}},"required":["champ","valeur_demandee","justification"],"type":"object"},"NewDeploiementReq":{"properties":{"cibles":{"description":"ids de things ciblés.","items":{"type":"string"},"type":"array"},"composants":{"description":"paires nom+version EXACTES de composants existants.","items":{"$ref":"#/components/schemas/ComposantReq"},"type":"array"}},"required":["cibles","composants"],"type":"object"},"NewDepotReq":{"properties":{"genre":{"type":"string"},"nom":{"type":"string"},"source":{"type":"string"}},"required":["nom","genre","source"],"type":"object"},"NewDistributionReq":{"properties":{"nom":{"type":"string"},"origine":{"type":"string"},"ttl_s":{"format":"int64","minimum":0,"type":"integer"}},"required":["nom","origine"],"type":"object"},"NewEvalReq":{"properties":{"agent":{"type":"string"},"cas":{"items":{"$ref":"#/components/schemas/CasEval"},"type":"array"},"nom":{"type":"string"}},"required":["nom","agent","cas"],"type":"object"},"NewFluxReq":{"properties":{"nb_partitions":{"minimum":0,"type":"integer"},"nom":{"type":"string"},"retention_heures":{"format":"int32","minimum":0,"type":"integer"}},"required":["nom"],"type":"object"},"NewFonctionReq":{"properties":{"code":{"type":"string"},"langage":{"type":"string"},"nom":{"type":"string"}},"required":["nom","langage","code"],"type":"object"},"NewGroupeFeaturesReq":{"properties":{"description":{"type":"string"},"features":{"items":{"type":"string"},"type":"array"},"nom":{"type":"string"}},"required":["nom","features"],"type":"object"},"NewGroupeReq":{"properties":{"cooldown_min":{"format":"int32","minimum":0,"type":"integer"},"gabarit_type":{"type":"string"},"image":{"type":"string"},"max":{"minimum":0,"type":"integer"},"min":{"minimum":0,"type":"integer"},"nom":{"type":"string"},"seuil_cpu_bas":{"format":"int32","minimum":0,"type":"integer"},"seuil_cpu_haut":{"format":"int32","minimum":0,"type":"integer"},"spot":{"description":"Les VMs du groupe seront spot (tarif réduit, préemptables).","type":"boolean"}},"required":["nom","gabarit_type","min","max"],"type":"object"},"NewHostReq":{"properties":{"gpu_declares":{"items":{"$ref":"#/components/schemas/GpuDeclare"},"type":"array"},"jeton":{"type":"string"},"nom":{"type":"string"},"note":{"type":"string"},"par_defaut":{"type":"boolean"},"quotas":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/QuotaHost"}]},"site":{"type":"string"},"url":{"type":"string"}},"required":["nom","url","jeton"],"type":"object"},"NewHoteReq":{"properties":{"disque_gb":{"description":"Disque (Go) déclaré du nœud — ignoré si la capacité réelle est\nfournie à la création.","format":"int32","minimum":0,"type":["integer","null"]},"noeud":{"description":"Nom du nœud Proxmox (1-30 car. [a-z0-9-.], existant).","type":"string"},"ram_mb":{"description":"RAM (Mo) déclarée du nœud — ignorée si la capacité réelle est\nfournie à la création.","format":"int32","minimum":0,"type":["integer","null"]},"vcpu_reserves":{"description":"vCPU réservés (1-64, ≤ capacité du nœud).","format":"int32","minimum":0,"type":"integer"}},"required":["noeud","vcpu_reserves"],"type":"object"},"NewInstanceReq":{"properties":{"genre":{"type":"string"},"k8s":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/K8sPlacementReq","description":"Placement k8s (0.89) : déploie l'instance en workload étiqueté\norg sur le cluster réel indiqué (Cilium requis pour la\nsegmentation) — au lieu d'un processus du nœud cirrod."}]},"noeuds":{"minimum":0,"type":"integer"},"nom":{"type":"string"}},"required":["nom","genre"],"type":"object"},"NewJobPdfReq":{"properties":{"destinataires":{"description":"Destinataires (≥ 1, adresses complètes).","items":{"type":"string"},"type":"array"},"planifie":{"description":"None = à la demande · « quotidien » | « hebdo » = scheduler.","type":["string","null"]}},"required":["destinataires"],"type":"object"},"NewJobReq":{"properties":{"commande":{"description":"Commande shell (non vide, ≤ 500 caractères).","type":"string"},"max_tentatives":{"format":"int32","minimum":0,"type":["integer","null"]},"nom":{"type":"string"},"planifie_le":{"description":"Exécution différée (RFC 3339) — absent = immédiate.","type":["string","null"]},"timeout_s":{"format":"int64","minimum":0,"type":["integer","null"]}},"required":["nom","commande"],"type":"object"},"NewK8sReq":{"properties":{"cilium":{"description":"true = nœuds LXC PRIVILÉGIÉS + flannel coupé + Cilium réel\n(micro-segmentation par org via CiliumNetworkPolicy — 0.89).","type":"boolean"},"noeuds":{"description":"Nombre de nœuds à provisionner (FIN2-K8S-001) — surcharge le\nnombre du plan (k8s-mini=1, k8s-ha=3, k8s-scale=5) ; borné par\n[`POOL_MAX_NOEUDS`]. Ignoré pour `k8s-vcluster` (refus 400).","format":"int64","type":["integer","null"]},"nom":{"type":"string"},"plan":{"description":"Plan OBLIGATOIRE (anomalie 9 du 15/09 : sans plan, la route\ncréait un enregistrement registre seul en prétendant un\ndéploiement). `k8s-vcluster` déploie un VRAI cluster virtuel ;\n`k8s-mini`/`k8s-ha`/`k8s-scale` sont déclaratifs (etat\n`declare` — nœuds LXC réels seulement avec `reel: true`).","type":["string","null"]},"reel":{"description":"true = nœuds LXC réels + k3s installé dessus (P12-07) ;\nfalse (défaut) = déclaratif (etat « declare », aucun nœud déployé).","type":"boolean"},"version":{"description":"Épingler une mineure à la création (défaut : dernière du catalogue)","type":["string","null"]}},"required":["nom"],"type":"object"},"NewLbReq":{"properties":{"adresse_ip":{"description":"IP d'écoute dédiée — doit appartenir au pool (CIRRO_LB_IPS),\nabsent ou vide = toutes interfaces (0.0.0.0), comportement\nhistorique.","type":["string","null"]},"cibles":{"items":{"type":"string"},"type":"array"},"nom":{"type":"string"},"port":{"description":"Port d'écoute — 0 = alloué depuis 19500.","format":"int32","minimum":0,"type":"integer"}},"required":["nom","cibles"],"type":"object"},"NewLlmEndpointReq":{"properties":{"modele":{"type":"string"},"nom":{"type":"string"},"quantification":{"type":"string"}},"required":["nom","modele"],"type":"object"},"NewMachineReq":{"properties":{"az":{"description":"AZ de la zone (nantes : durantiere | patureau | bois)","type":["string","null"]},"disque_gb":{"description":"Disque en Go (gigaoctets).","format":"int64","type":"integer"},"genre":{"description":"\"vm\" | \"lxc\"","type":"string"},"nom":{"type":"string"},"ram_mb":{"description":"RAM en Mo (mégaoctets, multiples de 1024 — Mio) ; servie telle\nquelle sous `ram_mb`, convertie en Go (arrondie au Go supérieur)\npour les budgets. CONFORMITE INCOHERENCES B6 (audit 16/09) :\nunité documentée.","format":"int64","type":"integer"},"reseau":{"description":"IPAM : nom du réseau de l'org auquel attacher la machine dès sa\ncréation (adresse réservée + posée sur net0 côté Proxmox).","type":["string","null"]},"vcpus":{"format":"int64","type":"integer"},"zone":{"description":"zone géographique — défaut nantes","type":["string","null"]}},"required":["nom","genre"],"type":"object"},"NewMemberReq":{"properties":{"email":{"type":"string"},"role":{"type":"string"}},"required":["email","role"],"type":"object"},"NewModeleReq":{"properties":{"description":{"type":"string"},"nom":{"type":"string"}},"required":["nom"],"type":"object"},"NewMotifLogReq":{"properties":{"composant":{"type":["string","null"]},"fenetre_s":{"format":"int64","type":"integer"},"id":{"type":"string"},"motif":{"type":"string"},"niveau_min":{"type":["string","null"]},"seuil":{"format":"int32","minimum":0,"type":"integer"},"seuil_retour":{"format":"int32","minimum":0,"type":"integer"}},"required":["id","motif","fenetre_s","seuil","seuil_retour"],"type":"object"},"NewNodePoolReq":{"properties":{"nb_max":{"format":"int64","type":"integer"},"nb_min":{"format":"int64","type":"integer"},"nom":{"type":"string"},"type_vm":{"description":"Type d'instance des nœuds (catalogue VM)","type":"string"}},"required":["nom"],"type":"object"},"NewOrgReq":{"properties":{"name":{"type":"string"},"slug":{"type":"string"}},"required":["slug","name"],"type":"object"},"NewPartageReq":{"properties":{"autorises":{"type":"string"},"nom":{"type":"string"},"taille_go":{"format":"int64","minimum":0,"type":"integer"}},"required":["nom"],"type":"object"},"NewPeerReq":{"properties":{"ip_tunnel":{"description":"IP dans le tunnel (ex. 10.99.0.2) — auto si vide.","type":["string","null"]},"nom":{"type":"string"}},"required":["nom"],"type":"object"},"NewPipelineReq":{"properties":{"cible_app":{"description":"Nom de l'app serverless ciblée.","type":"string"},"nom":{"type":"string"},"retour_arriere_auto":{"type":"boolean"},"source":{"$ref":"#/components/schemas/Source"},"strategie":{"description":"bleu_vert | canari (défaut bleu_vert).","type":["string","null"]},"trafic_canari":{"description":"1-99, exigée en canari.","format":"int32","minimum":0,"type":["integer","null"]},"url_verification":{"type":["string","null"]}},"required":["nom","source","cible_app"],"type":"object"},"NewPlageReq":{"properties":{"cidr":{"type":"string"},"nom":{"type":"string"}},"required":["nom","cidr"],"type":"object"},"NewPlanReq":{"properties":{"frequence":{"type":"string"},"heure_utc":{"format":"int32","minimum":0,"type":"integer"},"machine_id":{"type":"string"},"retention":{"format":"int32","minimum":0,"type":"integer"},"stockage_replique":{"description":"Identifiant d'un stockage PVE secondaire (réplication\ncross-site) — vide/absent : local seul.","type":["string","null"]}},"required":["machine_id","frequence"],"type":"object"},"NewPolitiqueReq":{"properties":{"bloquante":{"type":"boolean"},"contraintes":{"items":{"$ref":"#/components/schemas/Contrainte"},"type":"array"},"nom":{"type":"string"}},"required":["nom","contraintes"],"type":"object"},"NewPontMatrixReq":{"properties":{"agent":{"description":"Agent bridé (`ag-…`).","type":"string"},"politique":{"$ref":"#/components/schemas/PolitiqueReponse","description":"toujours | mention."},"salon":{"description":"Room id (`!abc:domaine`) ou alias (`#lab:domaine`).","type":"string"},"serveur":{"description":"Homeserver (`https://matrix.example.org`).","type":"string"}},"required":["agent","salon","serveur","politique"],"type":"object"},"NewProjectReq":{"properties":{"name":{"type":"string"},"sandbox":{"description":"Projet « bac à sable » : TTL 7 jours par défaut (EX-PRO-001)","type":"boolean"},"slug":{"type":"string"}},"required":["slug","name"],"type":"object"},"NewRapportReq":{"properties":{"nom":{"type":"string"},"periode_h":{"format":"int32","minimum":0,"type":"integer"},"planifie":{"description":"\"quotidien\", \"hebdo\" ou absent.","type":["string","null"]},"source":{"type":"string"}},"required":["nom","source"],"type":"object"},"NewRecordReq":{"properties":{"genre":{"type":"string"},"nom":{"type":"string"},"ttl":{"format":"int32","minimum":0,"type":"integer"},"valeur":{"type":"string"}},"required":["genre","nom","valeur"],"type":"object"},"NewRegleAlerteReq":{"description":"Corps de création d'une règle d'alerte.","properties":{"duree":{"description":"Échantillons consécutifs requis pour confirmer une transition.","format":"int32","minimum":0,"type":"integer"},"sens":{"description":"sup | inf — côté du déclenchement.","type":"string"},"serie":{"description":"Série surveillée (ex. cpu_utilisation).","type":"string"},"seuil":{"description":"Seuil de déclenchement.","format":"double","type":"number"},"seuil_retour":{"description":"Seuil de retour — STRICTEMENT du côté calme du seuil (sinon l'hystérésis serait nulle : refusée).","format":"double","type":"number"}},"required":["serie","sens","seuil","seuil_retour","duree"],"type":"object"},"NewRegleConfigReq":{"properties":{"actif":{"type":"boolean"},"genre":{"description":"tag_exige | rotation_secrets_max_jours | vm_taille_max |\nmachine_sans_savegarde | budget_obligatoire","type":"string"},"genres":{"description":"Genres de ressources ciblés (vide = tous, quand ça a du sens).","items":{"type":"string"},"type":"array"},"nom":{"type":"string"},"parametres":{"$ref":"#/components/schemas/Parametres"}},"required":["nom","genre"],"type":"object"},"NewRegleFeReq":{"properties":{"destination":{"type":"string"},"motif":{"type":"string"},"port":{"type":"string"},"protocole":{"type":"string"},"source":{"type":"string"},"statut":{"type":"string"}},"required":["source","destination","port","statut"],"type":"object"},"NewRegleFwReq":{"properties":{"action":{"type":"string"},"cible_vm":{"type":["string","null"]},"cidr":{"type":"string"},"direction":{"type":"string"},"nom":{"type":"string"},"port_max":{"format":"int64","type":["integer","null"]},"port_min":{"format":"int64","type":["integer","null"]},"priorite":{"format":"int64","type":"integer"},"protocole":{"type":"string"}},"required":["nom","direction","protocole"],"type":"object"},"NewRegleIotReq":{"properties":{"champ":{"description":"Champ du payload JSON (ex. « temperature »).","type":"string"},"comparateur":{"description":"> >= < <= = !=","type":"string"},"nom":{"description":"Nom parlant (2-60 caractères).","type":"string"},"seuil":{"format":"double","type":"number"},"topic_cible":{"description":"Topic de republication, sous lab/ (l'espace du broker).","type":"string"}},"required":["nom","champ","comparateur","seuil","topic_cible"],"type":"object"},"NewRegleL7Req":{"properties":{"action":{"type":"string"},"chemin":{"type":"string"},"chemin_regex":{"type":"boolean"},"entetes":{"items":{"type":"string"},"type":"array"},"limite_par_min":{"format":"int64","minimum":0,"type":"integer"},"methode":{"type":"string"},"nom":{"type":"string"},"patterns":{"items":{"type":"string"},"type":"array"},"priorite":{"format":"int64","type":"integer"},"taille_max_corps":{"format":"int64","minimum":0,"type":"integer"}},"required":["nom","chemin"],"type":"object"},"NewRegleNatReq":{"properties":{"cible_vm":{"description":"VM cible — OBLIGATOIRE pour dnat ET snat, avec une IP réelle\n(422 « cible sans IP réelle » sinon ; 404 si la VM n'existe pas)","type":["string","null"]},"nom":{"type":"string"},"port_externe":{"description":"dnat uniquement : port public exposé","format":"int64","type":["integer","null"]},"port_interne":{"description":"dnat uniquement : port de la VM cible","format":"int64","type":["integer","null"]},"sens":{"description":"dnat (port forwarding entrant) | snat (masquerade sortant de la cible)","type":"string"}},"required":["nom","sens"],"type":"object"},"NewRegleReq":{"properties":{"actif":{"type":"boolean"},"destination":{"$ref":"#/components/schemas/Destination"},"filtre":{"$ref":"#/components/schemas/FiltreEvenement"},"nom":{"type":"string"}},"required":["nom","filtre","destination"],"type":"object"},"NewRegleRicheReq":{"description":"Corps de création d'une règle à filtre riche.","properties":{"actif":{"type":"boolean"},"destination":{"$ref":"#/components/schemas/Destination"},"filtre":{"$ref":"#/components/schemas/FiltreRicheApiReq","description":"Filtre riche : genres (vide = tous) + conditions typées\n(chemin a.b.c, opérateur =, !=, >, >=, <, <=, ~= regex ancrée)."},"nom":{"type":"string"}},"required":["nom","filtre","destination"],"type":"object"},"NewReplicationReq":{"properties":{"digest_cible":{"type":["string","null"]},"digest_source":{"description":"`sha256:…` mesuré côté source (None = pas encore inspectée).","type":["string","null"]},"image_cible":{"type":"string"},"image_source":{"type":"string"}},"required":["image_source","image_cible"],"type":"object"},"NewRequeteReq":{"properties":{"nom":{"type":"string"},"sql":{"type":"string"}},"required":["nom","sql"],"type":"object"},"NewReseauReq":{"properties":{"cidr":{"type":"string"},"defaut_fe":{"description":"accept | reject — statut du trafic sans règle correspondante","type":"string"},"dhcp":{"type":"boolean"},"dns":{"items":{"type":"string"},"type":"array"},"isole":{"type":"boolean"},"nom":{"type":"string"},"passerelle":{"type":["string","null"]},"plage_dhcp":{"items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":["array","null"]},"vlan":{"description":"VLAN 802.1Q (1-4094) posé sur les machines attachées.","format":"int32","minimum":0,"type":["integer","null"]}},"required":["nom","cidr"],"type":"object"},"NewReservationReq":{"properties":{"duree_mois":{"format":"int32","minimum":0,"type":"integer"},"quantite":{"format":"int32","minimum":0,"type":"integer"},"type_vm":{"type":"string"}},"required":["type_vm","duree_mois"],"type":"object"},"NewRoomReq":{"description":"Corps de création d'une room.","properties":{"ttl_s":{"description":"Durée de vie de la room en secondes (bornes du noyau, défaut 5 min).","format":"int64","minimum":0,"type":["integer","null"]}},"type":"object"},"NewRouteReq":{"properties":{"hostname":{"type":"string"},"path":{"type":["string","null"]},"target_port":{"format":"int64","type":"integer"},"target_vm_id":{"type":"string"},"tls_mode":{"type":"string"}},"required":["hostname","target_vm_id"],"type":"object"},"NewSecretReq":{"properties":{"nom":{"type":"string"},"valeur":{"type":"string"}},"required":["nom","valeur"],"type":"object"},"NewSegmentAttributsReq":{"properties":{"nom":{"type":"string"},"predicat":{"description":"Prédicat sérialisable : {\"Egal\": {\"cle\": …, \"valeur\": …}} | {\"Dans\": …} | {\"Contient\": …} | {\"Non\": …} | {\"Et\": […]} | {\"Ou\": […]}."}},"required":["nom","predicat"],"type":"object"},"NewSegmentReq":{"properties":{"critere":{"description":"tous | admin | dev | lecteur","type":"string"},"nom":{"type":"string"}},"required":["nom","critere"],"type":"object"},"NewServeurReq":{"properties":{"cle_privee":{"type":["string","null"]},"hote":{"type":"string"},"mot_de_passe":{"type":"string"},"nom":{"type":"string"},"notes":{"type":"string"},"os":{"type":"string"},"port_ssh":{"format":"int32","minimum":0,"type":"integer"},"utilisateur":{"type":"string"}},"required":["nom","hote","utilisateur","mot_de_passe"],"type":"object"},"NewServiceK8sReq":{"properties":{"hostname":{"type":"string"},"nom":{"type":"string"},"port":{"format":"int64","type":"integer"},"tls":{"type":"boolean"}},"required":["nom","port","hostname"],"type":"object"},"NewSessionBgReq":{"properties":{"duree_min":{"format":"int64","type":["integer","null"]},"moitie_a":{"description":"Première moitié du scellé (gardien plateforme).","type":"string"},"moitie_b":{"description":"Seconde moitié du scellé (gardien opérateur).","type":"string"},"raison":{"type":"string"}},"required":["raison","moitie_a","moitie_b"],"type":"object"},"NewStackReq":{"properties":{"composants":{"$ref":"#/components/schemas/Composants"},"nom":{"type":"string"}},"required":["nom","composants"],"type":"object"},"NewStockReq":{"properties":{"emplacement":{"type":"string"},"etat":{"type":"string"},"genre":{"type":"string"},"modele":{"type":"string"},"nom":{"type":"string"},"notes":{"type":"string"},"quantite":{"format":"int64","type":"integer"}},"required":["nom","genre","modele"],"type":"object"},"NewSuperadminReq":{"properties":{"email":{"type":"string"}},"required":["email"],"type":"object"},"NewTableKvReq":{"properties":{"nom":{"type":"string"}},"required":["nom"],"type":"object"},"NewThingReq":{"properties":{"genre":{"type":"string"},"nom":{"type":"string"}},"required":["nom"],"type":"object"},"NewTicketReq":{"properties":{"description":{"type":"string"},"severite":{"description":"1-4 (le plan actif de l'org doit la couvrir).","format":"int32","minimum":0,"type":"integer"},"sujet":{"type":"string"}},"required":["sujet","description","severite"],"type":"object"},"NewTopicReq":{"properties":{"nom":{"type":"string"}},"required":["nom"],"type":"object"},"NewUserReq":{"properties":{"email":{"type":"string"},"password":{"type":"string"},"platform_admin":{"type":"boolean"}},"required":["email","password"],"type":"object"},"NewVersionDatasetReq":{"properties":{"empreinte":{"description":"Empreinte sha256 hex des octets du dataset — MESURÉE par\nl'intégrateur sur les octets réels.","type":"string"},"exemples":{"description":"Paires instruction/réponse comptées dans le dataset.","minimum":0,"type":"integer"},"note":{"type":"string"},"provenance":{"description":"D'où viennent les octets : `s3://…`, `upload:…`,\n`affinage:aft-…` — une référence vérifiable.","type":"string"}},"required":["empreinte","provenance","exemples"],"type":"object"},"NewVersionReq":{"properties":{"artefact":{"description":"Référence de l'artefact (bucket/…, URL, registre:tag).","type":"string"},"cadre":{"description":"Cadre : gguf, onnx, pytorch, safetensors…","type":"string"},"metriques":{"description":"Métriques libres (JSON)."},"note":{"type":"string"},"taille_mo":{"format":"int64","minimum":0,"type":"integer"}},"required":["cadre","artefact"],"type":"object"},"NewVmReq":{"properties":{"az":{"description":"AZ de la zone (ex. durantiere pour nantes) — optionnelle.","type":["string","null"]},"image":{"type":"string"},"labels":{"items":{"items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":"array"},"type":["array","null"]},"nom":{"type":"string"},"spot":{"description":"VM spot : tarif réduit (−70 %), préemptée en premier sous\npression budget (cirroScaling, 04/09).","type":"boolean"},"type":{"type":"string"},"zone":{"type":["string","null"]}},"required":["nom","type","image"],"type":"object"},"NewVolumeReq":{"properties":{"nom":{"type":"string"},"taille_gb":{"format":"int64","type":"integer"},"type_volume":{"type":"string"}},"required":["nom"],"type":"object"},"NewVoucherReq":{"properties":{"credit_euros":{"format":"double","type":"number"},"expire_le":{"type":["string","null"]},"libelle":{"type":"string"},"org":{"type":["string","null"]}},"required":["libelle","credit_euros"],"type":"object"},"NewVueReq":{"description":"Corps de déclaration d'une vue.","properties":{"nom":{"description":"Identifiant ferroquery de la vue ([a-z0-9_] 1-63, lettre en tête).","type":"string"},"table_source":{"description":"Table (ou vue amont) dont dépend cette vue.","type":"string"}},"required":["nom","table_source"],"type":"object"},"NewWarehouseReq":{"properties":{"nom":{"type":"string"},"taille_gb":{"format":"int64","type":"integer"}},"required":["nom"],"type":"object"},"NewWebhookDeliveryReq":{"properties":{"genres":{"description":"Genres suivis : delivery | bounce | complaint | reject.","items":{"type":"string"},"type":"array"},"url":{"type":"string"}},"required":["url","genres"],"type":"object"},"NewWebhookOrgReq":{"properties":{"evenements":{"items":{"type":"string"},"type":"array"},"url":{"type":"string"}},"required":["url"],"type":"object"},"NewWebhookReq":{"properties":{"events":{"items":{"type":"string"},"type":"array"},"nom":{"type":"string"},"url":{"type":"string"}},"required":["nom","url"],"type":"object"},"NewWorkflowReq":{"properties":{"description":{"type":"string"},"etapes":{"items":{"$ref":"#/components/schemas/Etape"},"type":"array"},"nom":{"type":"string"}},"required":["nom","etapes"],"type":"object"},"NewWorkspaceReq":{"properties":{"nom":{"type":"string"}},"required":["nom"],"type":"object"},"NewZoneReq":{"properties":{"nom":{"type":"string"}},"required":["nom"],"type":"object"},"NoeudFerrodbReq":{"description":"Nœud de topologie tel que la sonde l'a mesuré.","properties":{"etat":{"description":"sain | hs — mesuré par la sonde.","type":"string"},"id":{"type":"string"},"retard_ms":{"description":"Retard de réplication MESURÉ en ms (0 pour le primaire).","format":"int64","minimum":0,"type":"integer"},"role":{"description":"primaire | replica.","type":"string"}},"required":["id","role","retard_ms","etat"],"type":"object"},"NoeudInstance":{"properties":{"grpc":{"format":"int32","minimum":0,"type":"integer"},"id":{"format":"int64","minimum":0,"type":"integer"},"metrics":{"format":"int32","minimum":0,"type":"integer"},"pgwire":{"format":"int32","minimum":0,"type":"integer"}},"required":["id","pgwire","grpc","metrics"],"type":"object"},"NoeudUpgradeApiReq":{"description":"Un nœud de la montée : nom + rôle.","properties":{"nom":{"type":"string"},"role":{"description":"server-init | server-join | agent.","type":"string"}},"required":["nom","role"],"type":"object"},"NoeudsShardsReq":{"description":"Corps de pose d'une flotte de nœuds d'exécution pour les shards.","properties":{"noeuds":{"items":{"type":"string"},"type":"array"}},"required":["noeuds"],"type":"object"},"Note":{"properties":{"cree_le":{"type":"string"},"id":{"type":"string"},"run":{"description":"Run qui a écrit cette note (outil memo_ecrire).","type":["string","null"]},"texte":{"type":"string"}},"required":["id","texte","cree_le"],"type":"object"},"NoteReq":{"properties":{"texte":{"type":"string"}},"required":["texte"],"type":"object"},"NouvelleRevisionReq":{"properties":{"image":{"type":"string"},"trafic":{"description":"Part du trafic (0-100). Les autres révisions sont ajustées.","format":"int32","minimum":0,"type":"integer"}},"required":["image"],"type":"object"},"ObjetAvecAccesApiReq":{"description":"Miroir API d'un objet avec son accès mesuré (noyau sans ToSchema).","properties":{"cle":{"type":"string"},"dernier_acces":{"description":"Dernier accès en RFC 3339 — None : âge non mesuré.","type":["string","null"]},"taille_octets":{"format":"int64","type":"integer"}},"required":["cle","taille_octets"],"type":"object"},"ObjetChiffreApi":{"description":"Miroir API d'un objet chiffré — le noyau sérialise des octets nus,\nle transport les porte en base64.","properties":{"algo":{"description":"Toujours l'algo SSE du contrat — tout autre est refusé.","type":"string"},"chiffre_base64":{"description":"Contenu chiffré AES-256-GCM, tag d'authentification inclus (base64).","type":"string"},"cle_donnees_chiffree_base64":{"description":"Clé de données ENVELOPPÉE par la KEK de l'org (base64).","type":"string"},"cle_objet":{"description":"Clé de l'objet (liée par l'AAD).","type":"string"},"nonce_base64":{"description":"Nonce 96 bits (12 octets en base64).","type":"string"}},"required":["cle_objet","chiffre_base64","nonce_base64","cle_donnees_chiffree_base64","algo"],"type":"object"},"ObjetS3Export":{"description":"Un objet S3 embarqué (contenu intégral en base64).","properties":{"cle":{"type":"string"},"contenu_base64":{"type":"string"},"taille":{"format":"int64","minimum":0,"type":"integer"}},"required":["cle","taille","contenu_base64"],"type":"object"},"ObservationReplicationReq":{"properties":{"hors_ligne":{"type":"boolean"},"retard_ms":{"format":"int64","minimum":0,"type":"integer"}},"required":["retard_ms","hors_ligne"],"type":"object"},"OcrReq":{"properties":{"source":{"description":"Nom du fichier image d'origine (contexte du rapport).","type":["string","null"]},"texte_ocr":{"description":"Texte OCR DÉJÀ extrait par l'appelant — cirroSensible\nn'embarque PAS de moteur OCR, il n'en simule aucun.","type":"string"}},"required":["texte_ocr"],"type":"object"},"OidcAutoriserReq":{"properties":{"client_id":{"type":"string"},"issuer":{"type":"string"},"nonce":{"type":"string"},"redirect_uri":{"type":"string"},"scope":{"type":"string"},"state":{"type":"string"},"utilisateur":{"description":"Utilisateur d'app (`usr-…`) lié au flux — le sub de l'id_token.","type":["string","null"]}},"required":["issuer","client_id","redirect_uri","scope","state","nonce"],"type":"object"},"OidcCallbackReq":{"properties":{"code":{"description":"Le code d'autorisation renvoyé par le client.","type":"string"},"state":{"description":"Le state rappelé — doit être LE state ouvert (CSRF).","type":"string"}},"required":["code","state"],"type":"object"},"OidcTokenReq":{"properties":{"client_id":{"type":"string"},"code":{"type":"string"},"redirect_uri":{"type":"string"}},"required":["code","client_id","redirect_uri"],"type":"object"},"OptionSmbReq":{"properties":{"nom_partage":{"description":"Nom sous lequel les clients Windows verront le partage.","type":"string"}},"required":["nom_partage"],"type":"object"},"OrdreGlobalReq":{"description":"Corps d'activation/désactivation de l'ordre global d'un topic.","properties":{"active":{"type":"boolean"}},"required":["active"],"type":"object"},"Org":{"properties":{"created_at":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["id","slug","name","created_at"],"type":"object"},"OrgSuperadminVue":{"description":"Une organisation dans la vue superadmin.","properties":{"contact":{"description":"Contact de facturation tel que saisi (JSON libre, null si aucun)."},"cout_mois":{"description":"Coût du mois, même formule que la facture (€).","format":"double","type":"number"},"cree_le":{"type":"string"},"etat":{"description":"active | suspendue","type":"string"},"id":{"type":"string"},"membres":{"minimum":0,"type":"integer"},"membres_detail":{"items":{"$ref":"#/components/schemas/MembreDetailVue"},"type":"array"},"nb_ressources":{"description":"VMs + machines Proxmox + instances + IPv6 + serveurs externes.","minimum":0,"type":"integer"},"nom":{"type":"string"},"quotas":{"$ref":"#/components/schemas/QuotasVue"},"quotas_personnalises":{"description":"True si des quotas personnalisés existent (clé quotas:{slug}) —\nfalse = repli sur les défauts de plateforme.","type":"boolean"},"ressources":{"$ref":"#/components/schemas/RessourcesSuperadminVue"},"slug":{"type":"string"},"usage":{"$ref":"#/components/schemas/UsageSuperadminVue","description":"Usage réel des machines Proxmox de l'org (RAM en Mo ici)."}},"required":["id","slug","nom","cree_le","etat","membres","membres_detail","contact","nb_ressources","usage","cout_mois","quotas","quotas_personnalises","ressources"],"type":"object"},"OrgVue":{"description":"Identité de l'org dans la vue d'ensemble.","properties":{"name":{"type":"string"},"slug":{"type":"string"}},"required":["slug","name"],"type":"object"},"OrgsListe":{"description":"GET /v1/organizations — organisations visibles (toutes pour un admin\nplateforme, les siennes sinon).","properties":{"items":{"items":{"$ref":"#/components/schemas/Org"},"type":"array"},"total":{"minimum":0,"type":"integer"}},"required":["items","total"],"type":"object"},"OrigineReq":{"properties":{"echecs_consecutifs":{"format":"int32","minimum":0,"type":"integer"},"priorite":{"description":"1 = la plus préférée.","format":"int32","minimum":0,"type":"integer"},"saine":{"description":"Santé MESURÉE (dernière sonde).","type":"boolean"},"url":{"description":"URL de base http(s)://hote[:port].","type":"string"}},"required":["url","priorite"],"type":"object"},"Parametres":{"properties":{"cle":{"description":"Clé de tag exigée (règle `tag_exige`).","type":["string","null"]},"cpu_max":{"description":"vCPU max par VM (règle `vm_taille_max`).","format":"int64","type":["integer","null"]},"jours":{"description":"Jours de rotation max (règle `rotation_secrets_max_jours`).","format":"int64","type":["integer","null"]},"ram_mb_max":{"description":"RAM MiB max par VM (règle `vm_taille_max`).","format":"int64","type":["integer","null"]}},"type":"object"},"PartieUploadReq":{"description":"Une partie d'upload multipart (contenu encodé base64).","properties":{"contenu_base64":{"description":"Contenu de la partie, encodé base64.","type":"string"},"numero":{"description":"Numéro S3 de la partie (1..N consécutifs).","format":"int32","minimum":0,"type":"integer"}},"required":["numero","contenu_base64"],"type":"object"},"PartitionsAthenaReq":{"description":"Requête d'exécution partitionnée (CSV seulement — un document\nJSON unique ne se coupe pas par lignes, c'est dit côté noyau).","properties":{"contenu":{"type":"string"},"nb_partitions":{"description":"1-64 partitions équitables par lignes.","minimum":0,"type":"integer"},"nom":{"type":"string"},"sql":{"type":"string"}},"required":["nom","contenu","nb_partitions","sql"],"type":"object"},"PatchAgentReq":{"properties":{"description":{"type":["string","null"]},"instructions":{"type":["string","null"]},"llm_id":{"type":["string","null"]},"memoire":{"type":["boolean","null"]},"outils":{"items":{"type":"string"},"type":["array","null"]},"politique":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Politique"}]}},"type":"object"},"PdfVisionReq":{"properties":{"pdf_base64":{"type":"string"},"sorties_ocr":{"description":"Sorties OCR TSV par page (dans l'ordre), si un moteur a tourné.","items":{"type":"string"},"type":"array"}},"required":["pdf_base64"],"type":"object"},"PermissionReq":{"properties":{"service":{"type":"string"},"verbe":{"type":"string"}},"required":["service","verbe"],"type":"object"},"PieceJointeReq":{"description":"Une pièce jointe telle qu'attendue par la requête (miroir local de\n`courrier::PieceJointe` — le module ne dérive pas `ToSchema`, la\nconversion est mécanique champ à champ).","properties":{"contenu_base64":{"description":"Contenu binaire encodé base64 standard.","type":"string"},"nom":{"description":"Nom de fichier affiché (sans chemin, 1-255 caractères).","type":"string"},"type_mime":{"description":"Type MIME (ex. application/pdf).","type":"string"}},"required":["nom","type_mime","contenu_base64"],"type":"object"},"PiecePdfReq":{"properties":{"genere_le":{"description":"Horodatage RFC 3339 du rendu (défaut : maintenant).","type":["string","null"]},"nom":{"description":"Nom du fichier rendu (convention nom_piece si absent).","type":["string","null"]},"rendu_par":{"description":"CE qui a rendu la pièce (le binaire, mesuré, pas supposé).","type":"string"},"taille_octets":{"description":"Taille MESURÉE du fichier rendu (octets) — strictement positive.","format":"int64","minimum":0,"type":"integer"}},"required":["taille_octets","rendu_par"],"type":"object"},"PipelineMultiReq":{"description":"Corps API d'un pipeline multi-sources (miroir ToSchema de\n[`crate::dataflow::PipelineMulti`]).","properties":{"etapes":{"items":{"$ref":"#/components/schemas/EtapeEtlApiReq"},"type":"array"},"nom":{"type":"string"},"sources":{"items":{"$ref":"#/components/schemas/SourceMultiReq"},"type":"array"}},"required":["nom","sources","etapes"],"type":"object"},"PitrReq":{"description":"Corps d'une restauration PITR.","properties":{"base":{"description":"État de base avant rejeu : paires [table, ligne].","items":{"items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":"array"},"type":"array"},"journal":{"description":"Journal d'écritures (lsn croissants, ts RFC3339 non décroissants).","items":{"$ref":"#/components/schemas/EntreeJournalApiReq"},"type":"array"},"t_cible":{"description":"Instant cible RFC3339.","type":"string"}},"required":["journal","base","t_cible"],"type":"object"},"PlagePlanifiee":{"description":"Une plage horaire connue pour son niveau de charge : bornes min/max\nde VMs à appliquer quand le tick tombe dedans (heures UTC).","properties":{"debut_h":{"description":"Heure UTC de début (incluse), 0-23.","format":"int32","minimum":0,"type":"integer"},"fin_h":{"description":"Heure UTC de fin (EXCLUE), 0-23 — peut être < début (plage\nnocturne 22→6 qui passe minuit) ; égale à début = plage vide.","format":"int32","minimum":0,"type":"integer"},"max":{"description":"Maximum de VMs pendant la plage (écrêté par le max du groupe).","minimum":0,"type":"integer"},"min":{"description":"Minimum de VMs pendant la plage.","minimum":0,"type":"integer"},"nom":{"description":"Nom lisible (ex. \"bureau\", \"nuit\").","type":"string"}},"required":["nom","debut_h","fin_h","min","max"],"type":"object"},"Plan":{"properties":{"cout_cible_heure":{"format":"double","type":"number"},"cout_source_heure":{"format":"double","type":"number"},"economie_mois":{"description":"Économie mensuelle estimée (730 h).","format":"double","type":"number"},"manuelles":{"minimum":0,"type":"integer"},"ressources":{"items":{"$ref":"#/components/schemas/RessourcePlan"},"type":"array"}},"required":["ressources","manuelles","cout_source_heure","cout_cible_heure","economie_mois"],"type":"object"},"PlanAuthAppReq":{"properties":{"liaison":{"description":"oidc | jetons.","type":"string"}},"required":["liaison"],"type":"object"},"PlanBuildReq":{"description":"Corps d'une demande de plan de build.","properties":{"fichiers":{"description":"Manifeste (noms de fichiers à la racine du dépôt).","items":{"type":"string"},"type":"array"},"langage":{"description":"Langage détecté (python, nodejs, rust, go, statique).","type":"string"},"procfile":{"description":"CONTENU du Procfile quand il existe (le manifeste ne porte que des noms).","type":["string","null"]}},"required":["langage","fichiers"],"type":"object"},"PlanChaosReq":{"properties":{"actif":{"type":"boolean"},"duree_max_min":{"description":"Durée maximale (minutes, 1-240).","format":"int64","minimum":0,"type":"integer"},"heure":{"description":"Heure UTC (0-23) — défaut 02 h.","format":"int32","minimum":0,"type":"integer"},"jour_semaine":{"description":"0 (lundi) … 6 (dimanche) — défaut samedi.","format":"int32","minimum":0,"type":"integer"},"zone":{"description":"Zone/nœud visé par l'expérience (ex. sim-a).","type":"string"}},"required":["zone","duree_max_min"],"type":"object"},"PlanExercice":{"description":"Plan d'exercices blancs : occurrences régulières, dérivées.","properties":{"intervalle_s":{"description":"Intervalle entre occurrences (secondes, ≥ 1).","format":"int64","minimum":0,"type":"integer"},"org":{"type":"string"},"premiere_le":{"description":"Première occurrence (epoch secondes).","format":"int64","minimum":0,"type":"integer"}},"required":["org","premiere_le","intervalle_s"],"type":"object"},"PlanFuseReq":{"description":"Corps d'une simulation de montage FUSE.","properties":{"lectures":{"description":"Lectures à rejouer (mesurées par l'appelant).","items":{"$ref":"#/components/schemas/LectureFuseReq"},"type":"array"},"plafond_total_octets":{"description":"Plafond global téléchargeable (None = illimité — dit tel).","format":"int64","minimum":0,"type":["integer","null"]},"point_montage":{"description":"Point de montage ABSOLU côté client.","type":"string"}},"required":["point_montage","lectures"],"type":"object"},"PlanIacReq":{"properties":{"souhaite":{"description":"L'état souhaité — chaque ligne (famille, id, nom, type, taille).","items":{"$ref":"#/components/schemas/RessourceDeclareeReq"},"type":"array"}},"required":["souhaite"],"type":"object"},"PlanIngestionS3Req":{"properties":{"bucket":{"type":"string"},"cle":{"type":"string"},"contenu_texte":{"description":"Contenu texte de l'objet quand on l'a déjà (petit CSV/JSONL) —\nc'est de LUI que les lignes attendues se comptent.","type":["string","null"]},"format":{"description":"csv | jsonl | parquet | arrow.","type":"string"},"table":{"description":"Table cible dans l'entrepôt ([a-z0-9_], lettre en tête).","type":"string"},"taille_octets":{"description":"Taille HEAD si l'intégration l'a mesurée (informative — jamais\nconvertie en « lignes attendues » : ça serait déduire).","format":"int64","minimum":0,"type":["integer","null"]}},"required":["bucket","cle","format","table"],"type":"object"},"PlanMigrationReq":{"description":"Corps d'une demande de plan de migration.","properties":{"fenetre":{"$ref":"#/components/schemas/FenetreApiReq","description":"Fenêtre de maintenance (heures UTC 0-23, début ≠ fin)."},"hote_source":{"description":"Hôte source à évacuer (1-60 caractères).","type":"string"},"vms":{"description":"VMs à migrer avec leurs charges et durées MESURÉES.","items":{"$ref":"#/components/schemas/VmAMigrerApiReq"},"type":"array"}},"required":["hote_source","fenetre","vms"],"type":"object"},"PlanReplicationReq":{"description":"Corps d'une demande de plan de réplication.","properties":{"cible":{"$ref":"#/components/schemas/CibleReplicationApiReq","description":"Cible froide (racine absolue + sous-dossier org)."},"empreinte_cible":{"description":"Empreinte sha256 MESURÉE côté cible — None si l'archive y est absente.","type":["string","null"]}},"required":["cible"],"type":"object"},"PlanRestaurationNfsReq":{"properties":{"chemin":{"description":"Chemin du fichier à restaurer.","type":"string"},"existe_actuellement":{"description":"Un fichier existe-t-il DÉJÀ à la destination (mesuré côté\npartage par l'appelant) — il sera écrasé, dit avant l'action.","type":"boolean"},"snapshot_id":{"description":"Snapshot source (id).","type":"string"}},"required":["snapshot_id","chemin","existe_actuellement"],"type":"object"},"PlanSauvegardeGitea":{"description":"Plan de sauvegarde de la forge : miroir des données Gitea\n(dépôts git inclus) vers le NAS. rsync = copie miroir exacte ;\nborg = archives versionnées, chiffrées et vérifiables. La\nfidélité se PROUVE par empreinte : sha256 mesurée de part et\nd'autre, comparée — jamais déclarée.","properties":{"cible":{"description":"Cible NAS : dossier du miroir (rsync) ou du dépôt d'archives (borg).","type":"string"},"conserver":{"description":"borg : nombre d'archives conservées (pruning), ≥ 2.","format":"int32","minimum":0,"type":"integer"},"derniere_empreinte":{"description":"Empreinte sha256 de la dernière sauvegarde VÉRIFIÉE — mesurée\npar les commandes du plan, None si jamais vérifiée.","type":["string","null"]},"frequence_heures":{"description":"Fréquence en heures (24 = quotidien ; 1-720 admis).","format":"int32","minimum":0,"type":"integer"},"methode":{"description":"\"rsync\" | \"borg\" (cf. METHODES_SAUVEGARDE_GITEA).","type":"string"},"org":{"type":"string"}},"required":["org","methode","frequence_heures","cible","conserver"],"type":"object"},"PlanScanTrivyReq":{"properties":{"image":{"description":"Référence d'image à scanner (ex. `ghcr.io/lab/web:1.4.0`).","type":"string"}},"required":["image"],"type":"object"},"PlanTestRestaurationReq":{"properties":{"fenetre":{"description":"Fenêtre cron 5 champs, formes simples (ex. « */5 3 * * * »).","type":"string"},"machine_temoin":{"description":"Machine jetable sur laquelle restaurer (jamais la production).","type":"string"},"plan_backup":{"description":"Id du plan de sauvegarde testé (bkp-…) — doit exister VRAIMENT.","type":"string"}},"required":["plan_backup","machine_temoin","fenetre"],"type":"object"},"PlanUsecaseReq":{"properties":{"valeurs":{"additionalProperties":{"type":"string"},"description":"Valeurs {{clé → valeur}} (les absentes prennent le défaut).","propertyNames":{"type":"string"},"type":"object"}},"type":"object"},"PluginVueReq":{"properties":{"champs":{"items":{"$ref":"#/components/schemas/ChampPluginReq"},"type":"array"},"description":{"description":"Ce que la vue montre (une phrase).","type":"string"},"nom":{"description":"Nom slug du plugin (2-40 [a-z0-9_-], sans chiffre en tête).","type":"string"}},"required":["nom","description","champs"],"type":"object"},"PodAutopilotApiReq":{"description":"Miroir API d'un pod du manifeste autopilot (noyau sans ToSchema).","properties":{"cpu_milli":{"description":"CPU demandé par réplique (milli-cœurs).","format":"int64","type":"integer"},"nom":{"type":"string"},"ram_mo":{"description":"RAM demandée par réplique (Mo).","format":"int64","type":"integer"},"replicas":{"format":"int32","minimum":0,"type":"integer"}},"required":["nom","replicas","cpu_milli","ram_mo"],"type":"object"},"PoidsTraficReq":{"description":"Poids de trafic à poser (chaque entrée : id de révision, part %).","properties":{"poids":{"description":"(id de révision, part en %) — la somme fait exactement 100.","items":{"items":false,"prefixItems":[{"type":"string"},{"format":"int32","minimum":0,"type":"integer"}],"type":"array"},"type":"array"}},"required":["poids"],"type":"object"},"Politique":{"properties":{"autorisations":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PolitiqueOutils","description":"Autorisations granulaires par outil et par argument (action\nA5#4, tranche 5). `None` : comportement inchangé, la sélection\net `lecture_seule` seuls tranchent. `Some(…)` : la couche\ntranche les OUTILS en plus — lecture seule par défaut (un outil\nd'action exige une règle explicite), refus nommant l'outil."}]},"cout_max_euros":{"description":"Budget € maximum du run (estimé AVANT chaque complétion).","format":"double","type":"number"},"lecture_seule":{"description":"Vrai = AUCUN outil d'action ne s'exécute (guardrail absolu).","type":"boolean"},"max_etapes":{"description":"Nombre d'étapes (appels LLM) maximum, 1..=ETAPES_MAX.","format":"int32","minimum":0,"type":"integer"},"outils_interdits":{"description":"Outils explicitement interdits en plus de la sélection.","items":{"type":"string"},"type":"array"},"tokens_max":{"description":"Budget de JETONS maximum du run (in+out cumulés, comptés à\nCHAQUE complétion sur l'usage rendu par le moteur) — l'action\nZ7 : un compteur mesuré, pas une estimation.","format":"int64","type":"integer"}},"required":["lecture_seule","max_etapes","cout_max_euros","outils_interdits"],"type":"object"},"PolitiqueActionsReq":{"description":"La politique d'actions d'un budget à périmètre.","properties":{"seuil_alerte_pct":{"format":"int64","minimum":0,"type":"integer"},"seuil_gel_pct":{"format":"int64","minimum":0,"type":"integer"}},"required":["seuil_alerte_pct","seuil_gel_pct"],"type":"object"},"PolitiqueOutils":{"description":"Politique d'outils granulaire d'un agent : LECTURE SEULE PAR\nDÉFAUT — tout outil d'action est refusé tant qu'aucune règle ne\nl'autorise explicitement ; un outil de lecture passe sauf règle\nl'interdisant.","properties":{"regles":{"items":{"$ref":"#/components/schemas/RegleOutil"},"type":"array"}},"required":["regles"],"type":"object"},"PolitiqueReponse":{"description":"Quand le pont déclenche l'agent.","enum":["toujours","mention"],"type":"string"},"PolitiqueRetentionSerde":{"properties":{"composant":{"type":"string"},"retention_s":{"format":"int64","type":"integer"}},"required":["composant","retention_s"],"type":"object"},"PolitiqueRouteDnsReq":{"description":"Corps API d'une politique de routage de zone (miroir ToSchema).","properties":{"genre":{"description":"geo | latence.","type":"string"},"pools":{"items":{"$ref":"#/components/schemas/PoolRouteDnsReq"},"type":"array"},"region_repli":{"description":"Région de repli (failover) quand le pool choisi est MUET.","type":["string","null"]}},"required":["genre","pools"],"type":"object"},"PolitiqueSnapshotsReq":{"properties":{"conservation_jours":{"description":"Jours de conservation avant rotation.","format":"int32","minimum":0,"type":"integer"},"intervalle_h":{"description":"Heures entre deux snapshots (24 = quotidien).","format":"int32","minimum":0,"type":"integer"}},"required":["intervalle_h","conservation_jours"],"type":"object"},"PolitiqueTagsReq":{"description":"La demande de pose d'une politique de tags.","properties":{"cles_exigees":{"description":"Clés exigées sur toute nouvelle ressource (≤ 10, uniques).","items":{"type":"string"},"type":"array"}},"required":["cles_exigees"],"type":"object"},"PolitiqueTieringReq":{"description":"Corps de pose d'une politique de tiering.","properties":{"age_jours_sans_acces":{"description":"Âge SANS ACCÈS (jours) déclenchant la transition, ≥ 1.","format":"int32","minimum":0,"type":"integer"},"classe_cible":{"description":"Classe cible : froid | glacier.","type":"string"}},"required":["age_jours_sans_acces","classe_cible"],"type":"object"},"PontFluxReq":{"properties":{"entrepot":{"description":"Entrepôt cible (wh-…).","type":"string"},"table":{"description":"Table de destination dans l'entrepôt ([a-z0-9_], lettre en tête).","type":"string"}},"required":["entrepot","table"],"type":"object"},"PoolRouteDnsReq":{"description":"Corps API d'un pool régional (miroir ToSchema).","properties":{"cibles":{"description":"Cibles du pool : les valeurs A rendues (IPv4).","items":{"type":"string"},"type":"array"},"region":{"description":"Nom de région (ex. « eu-ouest ») — unique dans la politique.","type":"string"},"sources":{"description":"CIDR sources desservis par ce pool (géoroutage).","items":{"type":"string"},"type":"array"}},"required":["region","sources","cibles"],"type":"object"},"PoserAttributsReq":{"properties":{"attributs":{"additionalProperties":{"type":"string"},"description":"Remplacement COMPLET des attributs du membre.","propertyNames":{"type":"string"},"type":"object"}},"required":["attributs"],"type":"object"},"PoserCleKvReq":{"properties":{"ttl_s":{"description":"TTL en secondes — absent = sans expiration.","format":"int64","minimum":0,"type":["integer","null"]},"valeur":{}},"required":["valeur"],"type":"object"},"PoserCouvertureReq":{"properties":{"demandes":{"description":"Demandes (genre app | zone_dns | topic + id).","items":{"$ref":"#/components/schemas/DemandeCouvertureReq"},"type":"array"}},"required":["demandes"],"type":"object"},"PoserLabelsCarteReq":{"properties":{"labels":{"additionalProperties":{"type":"string"},"description":"Labels à poser (remplacement complet pour ce service).","propertyNames":{"type":"string"},"type":"object"},"nom":{"description":"Nom du service dans la zone (nom RELATIF sanitisé, ex. `api-prod`).","type":"string"}},"required":["nom","labels"],"type":"object"},"PoserLimitesFonctionReq":{"properties":{"duree_max_ms":{"format":"int64","minimum":0,"type":"integer"},"memoire_max_mo":{"format":"int64","minimum":0,"type":"integer"}},"required":["duree_max_ms","memoire_max_mo"],"type":"object"},"PoserLimitesReq":{"properties":{"concurrence_par_instance":{"description":"Requêtes simultanées admises par instance.","format":"int32","minimum":0,"type":"integer"},"timeout_requete_s":{"description":"Timeout d'une requête (secondes) — défaut 30 si absent.","format":"int64","minimum":0,"type":["integer","null"]}},"required":["concurrence_par_instance"],"type":"object"},"PoserProfilVpnReq":{"properties":{"expire_le":{"description":"Epoch d'expiration (0 = jamais) — ignoré si absent.","format":"int64","minimum":0,"type":["integer","null"]},"nom":{"description":"Nom d'affichage du profil (posé à la création).","type":["string","null"]},"revoque":{"description":"Révocation administrative (prime sur l'expiration).","type":["boolean","null"]}},"type":"object"},"PoserSanteReq":{"properties":{"chemin":{"description":"Chemin relatif sondé (ex. /healthz).","type":"string"},"intervalle_s":{"description":"Secondes entre deux sondes (bornes HC_INTERVALLE_*).","format":"int64","minimum":0,"type":"integer"}},"required":["chemin","intervalle_s"],"type":"object"},"PoserTagReq":{"properties":{"cle":{"type":"string"},"ressource":{"description":"Ressource ciblée : id de VM, machine, réseau, volume, instance…","type":"string"},"valeur":{"type":"string"}},"required":["ressource","cle","valeur"],"type":"object"},"PoserVerrouReq":{"properties":{"cle":{"description":"Clé de l'objet — chaîne opaque (les `/` en font partie).","type":"string"},"echeance":{"description":"Fin de rétention, RFC 3339 STRICTEMENT future.","type":"string"}},"required":["cle","echeance"],"type":"object"},"PostMortemChaosReq":{"properties":{"chronologie":{"items":{"$ref":"#/components/schemas/EvenementChronoReq"},"type":"array"},"debut_le":{"type":"string"},"experience_id":{"type":"string"},"fin_le":{"type":"string"},"recuperations":{"items":{"$ref":"#/components/schemas/RecuperationReq"},"type":"array"},"zone":{"type":"string"}},"required":["experience_id","zone","debut_le","fin_le"],"type":"object"},"PostMortemReq":{"description":"La demande de publication d'un post-mortem.","properties":{"actions":{"description":"Actions correctives décidées.","items":{"type":"string"},"type":"array"},"cause":{"description":"Cause racine constatée.","type":"string"},"incident_id":{"description":"Incident concerné (inc-… / id d'anomalie ouverte).","type":"string"},"resume":{"description":"Résumé factuel (10-2 000 caractères).","type":"string"}},"required":["incident_id","resume","cause"],"type":"object"},"PrioriteReq":{"properties":{"niveau":{"description":"0 = haute, 1 = normale (défaut), 2 = basse.","format":"int32","minimum":0,"type":"integer"}},"required":["niveau"],"type":"object"},"Project":{"properties":{"created_at":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"org_id":{"type":"string"},"slug":{"type":"string"},"ttl_expires_at":{"type":["string","null"]}},"required":["id","org_id","slug","name","created_at"],"type":"object"},"PromouvoirReq":{"properties":{"vers":{"description":"Cible : staging ou prod.","type":"string"},"version":{"format":"int32","minimum":0,"type":"integer"}},"required":["version","vers"],"type":"object"},"PublierEnLigneReq":{"properties":{"entite":{"type":"string"},"expire_dans_s":{"description":"Expiration en secondes — absent = sans expiration.","format":"int64","minimum":0,"type":["integer","null"]},"groupe":{"type":"string"},"valeurs":{"additionalProperties":{"$ref":"#/components/schemas/ValeurFeature"},"propertyNames":{"type":"string"},"type":"object"}},"required":["groupe","entite","valeurs"],"type":"object"},"PublierReq":{"properties":{"message":{"description":"Charge utile libre (JSON)."},"sujet":{"type":"string"}},"required":["sujet","message"],"type":"object"},"PublierThingReq":{"properties":{"payload":{"description":"Payload texte publié tel quel sur le topic cmd du thing.","type":"string"}},"required":["payload"],"type":"object"},"QuotaHost":{"description":"Quota d'allocation Cirrostratus sur UN host (plafond par host).","properties":{"disque_gb":{"description":"Plafond disque en Go.","format":"int64","type":"integer"},"ram_gb":{"description":"Plafond RAM du host en Go (gigaoctets). CONFORMITE INCOHERENCES\nB6 (audit 16/09) : les quantités RAM en Mo sont servies sous\n`ram_mb`/`ram_mo` (alias), les plafonds en Go sous `ram_gb` —\nunité documentée ici.","format":"int64","type":"integer"},"vcpus":{"format":"int64","type":"integer"}},"required":["vcpus","ram_gb","disque_gb"],"type":"object"},"QuotasVue":{"description":"Quotas effectifs de l'org (défauts ou personnalisés).","properties":{"disque_gb":{"format":"int64","type":"integer"},"nb_vms":{"format":"int64","type":"integer"},"ram_mb":{"format":"int64","type":"integer"},"vcpu":{"format":"int64","type":"integer"}},"required":["vcpu","ram_mb","disque_gb","nb_vms"],"type":"object"},"RapportTrivyReq":{"properties":{"rapport_json":{"description":"Contenu du rapport JSON produit par la commande planifiée.","type":"string"},"seuil":{"description":"Niveau minimal qui bloque la synchronisation (None = informatif).","type":["string","null"]}},"required":["rapport_json"],"type":"object"},"RechercheReq":{"properties":{"genre":{"description":"Filtre par genre (vide = tous).","type":["string","null"]},"limite":{"description":"Nombre max de résultats.","minimum":0,"type":"integer"},"query":{"description":"Texte cherché (nom, tags, détail).","type":"string"}},"required":["query"],"type":"object"},"RecoMenageReq":{"properties":{"confirmee":{"description":"Confirmation humaine — rien ne s'applique sans elle.","type":"boolean"},"detail":{"type":"string"},"economie_e_mois":{"format":"double","type":"number"},"genre":{"type":"string"},"ressource":{"type":"string"}},"required":["ressource","genre"],"type":"object"},"RecuperationReq":{"properties":{"cible":{"type":"string"},"recuperee_le":{"description":"RFC 3339 — la cible est repartie à cet instant.","type":"string"}},"required":["cible","recuperee_le"],"type":"object"},"RefreshVueReq":{"description":"Corps d'un refresh incrémental.","properties":{"horodatages_source":{"description":"Horodatages RFC3339 des lignes de la source — MESURÉS par l'appelant.","items":{"type":"string"},"type":"array"},"lignes_deja_integrees":{"description":"Lignes déjà intégrées par les refreshs précédents (compté).","format":"int64","minimum":0,"type":"integer"}},"required":["horodatages_source","lignes_deja_integrees"],"type":"object"},"RefuserReq":{"properties":{"motif":{"type":"string"}},"required":["motif"],"type":"object"},"RegleCacheReq":{"properties":{"cacher":{"description":"true = mettre en cache ; false = BYPASS.","type":"boolean"},"condition":{"description":"Condition sérialisée : {\"EnTete\": {\"nom\": …, \"valeur\": …}} | {\"Cookie\": {\"nom\": …}} | {\"Query\": {\"cle\": …}} | {\"Chemin\": {\"prefixe\": …}}."},"id":{"type":"string"},"ttl_s":{"format":"int64","minimum":0,"type":["integer","null"]}},"required":["id","condition","cacher"],"type":"object"},"RegleClaimSerde":{"properties":{"claim":{"type":"string"},"priorite":{"format":"int32","minimum":0,"type":"integer"},"role":{"type":"string"},"valeur":{"description":"Vide = la présence du claim suffit.","type":"string"}},"required":["priorite","claim","valeur","role"],"type":"object"},"RegleCustomReq":{"description":"La demande de création d'une règle custom.","properties":{"expression":{"description":"Expression « champ comparateur valeur » (ex. « score > 80 »,\n« genre = rafale_429 », « cible_nom != api »).","type":"string"},"nom":{"type":"string"},"severite":{"description":"Sévérité portée par le déclenchement (1-10).","format":"int64","type":"integer"}},"required":["nom","expression","severite"],"type":"object"},"RegleCycleVieReq":{"properties":{"age_jours":{"description":"Âge minimal en jours (mesuré sur la dernière modification).","format":"int32","minimum":0,"type":"integer"},"id":{"type":"string"},"prefixe":{"description":"Préfixe de clé (vide = tout le bucket).","type":"string"},"retention_jours":{"format":"int32","minimum":0,"type":["integer","null"]}},"required":["id","age_jours"],"type":"object"},"RegleFe":{"properties":{"cree_le":{"type":"string"},"destination":{"description":"IP, CIDR ou « any »","type":"string"},"id":{"type":"string"},"motif":{"description":"Pourquoi cette règle (audit)","type":"string"},"port":{"description":"« 22 » | « 80,443 » | « 3000-4000 » | « any »","type":"string"},"protocole":{"description":"tcp | udp | any","type":"string"},"source":{"description":"IP, CIDR ou « any »","type":"string"},"statut":{"description":"accept | reject","type":"string"}},"required":["id","source","destination","protocole","port","statut","cree_le"],"type":"object"},"RegleL7":{"properties":{"action":{"description":"bloquer | limiter | journaliser","type":"string"},"chemin":{"description":"Préfixe de chemin (« /v1/admin ») ou regex (« ^/v1/x/.*$ »).","type":"string"},"chemin_regex":{"description":"true = chemin est une regex.","type":"boolean"},"created_at":{"type":"string"},"declenchements":{"format":"int64","minimum":0,"type":"integer"},"entetes":{"description":"En-têtes exigés/interdits : \"User-Agent=sqlmap\" (interdit si prefixé !).","items":{"type":"string"},"type":"array"},"etat":{"type":"string"},"id":{"type":"string"},"limite_par_min":{"description":"Requêtes/minute tolérées quand action=limiter.","format":"int64","minimum":0,"type":"integer"},"methode":{"description":"Méthode HTTP filtrée, \"*\" pour toutes.","type":"string"},"nom":{"type":"string"},"patterns":{"description":"Patterns de charge utile : sql | xss | traversal | (libre : regex).","items":{"type":"string"},"type":"array"},"priorite":{"format":"int64","type":"integer"},"taille_max_corps":{"description":"Taille maximale du corps en octets (0 = illimitée).","format":"int64","minimum":0,"type":"integer"}},"required":["id","nom","methode","chemin","chemin_regex","entetes","taille_max_corps","patterns","action","limite_par_min","priorite","etat","created_at","declenchements"],"type":"object"},"RegleLimitationReq":{"properties":{"cible":{"description":"ip | chemin.","type":"string"},"fenetre_s":{"description":"Largeur de la fenêtre glissante (secondes, 1-3600).","format":"int64","minimum":0,"type":"integer"},"max":{"description":"Requêtes maximum dans la fenêtre.","format":"int32","minimum":0,"type":"integer"},"nom":{"type":"string"},"valeur":{"description":"IP exacte (cible ip) ou préfixe de chemin (cible chemin).","type":"string"}},"required":["nom","cible","valeur","max","fenetre_s"],"type":"object"},"RegleOutil":{"description":"Une règle d'outil : autorisé ou interdit, avec ses arguments\ninterdits (même quand l'outil lui-même passe).","properties":{"args_interdits":{"description":"Noms d'arguments interdits POUR CET outil — ils doivent être des\narguments DÉCLARÉS au catalogue (validé, pas de règle fantôme).","items":{"type":"string"},"type":"array"},"autorise":{"type":"boolean"},"outil":{"type":"string"}},"required":["outil","autorise"],"type":"object"},"RegleRoutageReq":{"properties":{"canal_digest":{"description":"Canal du digest (tout ce qui n'urge pas).","type":"string"},"canal_push":{"description":"Canal des notifications immédiates (ex. webhook-urgent).","type":"string"},"seuil_push":{"description":"Les sévérités 1…seuil (incluses) poussent en immédiat (1-4).","format":"int32","minimum":0,"type":"integer"}},"required":["seuil_push","canal_push","canal_digest"],"type":"object"},"RegleSg":{"description":"Une règle d'autorisation du groupe (allow-only). `cidr` désigne\nl'AUTRE bout : source en ingress, destination en egress.","properties":{"cidr":{"description":"CIDR de l'autre bout (source en ingress, destination en egress).","type":"string"},"direction":{"description":"ingress | egress","type":"string"},"id":{"type":"string"},"port_max":{"format":"int32","minimum":0,"type":["integer","null"]},"port_min":{"description":"Plage de ports couverte par la règle (None pour icmp/all).","format":"int32","minimum":0,"type":["integer","null"]},"protocole":{"description":"tcp | udp | icmp | all","type":"string"}},"required":["id","direction","protocole","cidr"],"type":"object"},"RegleTransformationReq":{"description":"Corps API d'une règle de transformation (miroir ToSchema).","properties":{"ajouter_en_tetes":{"description":"En-têtes posés APRÈS les retraits (paires [nom, valeur]).","items":{"items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":"array"},"type":"array"},"chemin_cible":{"description":"Ce qui remplace le préfixe (commence par /).","type":"string"},"nom":{"type":"string"},"prefixe":{"description":"Préfixe du chemin qui déclenche la règle (2-200 caractères, commence par /).","type":"string"},"retirer_en_tetes":{"description":"En-têtes retirés AVANT les ajouts (noms).","items":{"type":"string"},"type":"array"}},"required":["nom","prefixe","chemin_cible"],"type":"object"},"RegleTransformationReqArray":{"description":"Enveloppe du jeu de règles (miroir ToSchema).","properties":{"regles":{"items":{"$ref":"#/components/schemas/RegleTransformationReq"},"type":"array"}},"required":["regles"],"type":"object"},"RejeuEdgeReq":{"properties":{"cloud":{"description":"L'état du cloud : (clé d'idempotence, empreinte sha256 de la\ncharge APPLIQUÉE) pour chaque action déjà reçue.","items":{"items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":"array"},"type":"array"}},"required":["cloud"],"type":"object"},"RejouerFenetreReq":{"properties":{"depuis_seq":{"description":"Séquence de départ EXCLUSIVE (0 = depuis le début retenu).","format":"int64","minimum":0,"type":"integer"},"jusqu_a_seq":{"description":"Séquence de fin INCLUSIVE — absent = jusqu'à la tête.","format":"int64","minimum":0,"type":["integer","null"]},"max":{"description":"Maximum d'événements rendus.","minimum":0,"type":"integer"}},"type":"object"},"ReleveTunnelReq":{"properties":{"dernier_handshake":{"description":"Dernier handshake (epoch, 0 = jamais).","format":"int64","minimum":0,"type":"integer"},"rx_octets":{"format":"int64","minimum":0,"type":"integer"},"tx_octets":{"format":"int64","minimum":0,"type":"integer"}},"required":["rx_octets","tx_octets","dernier_handshake"],"type":"object"},"RemediationConfigReq":{"description":"La demande de pose de la politique de remediation (une action par\nrègle visée — remplace la liste entière).","properties":{"politiques":{"description":"Une entrée par règle : `{\"regle_id\":\"…\",\"action\":{\"tagger\":{\"cle\":\"env\",\"valeur\":\"prod\"}}}` ou `{\"action\":\"notifier\"}`.","items":{},"type":"array"}},"required":["politiques"],"type":"object"},"RendreTemplateReq":{"description":"La demande de rendu d'un template.","properties":{"variables":{"additionalProperties":{"type":"string"},"description":"Valeurs des variables (`{{clé}}` → valeur) — toute variable du\ngabarit sans valeur est une erreur NOMMÉE.","propertyNames":{"type":"string"},"type":"object"}},"required":["variables"],"type":"object"},"ReponseRejeuReq":{"properties":{"id":{"type":"string"},"question":{"type":"string"},"reponse":{"type":"string"}},"required":["id","question","reponse"],"type":"object"},"ReponsesQuizReq":{"properties":{"reponses":{"items":{"minimum":0,"type":"integer"},"type":"array"}},"required":["reponses"],"type":"object"},"ReqImport":{"description":"Corps de la requête d'import (superadmin).","properties":{"bundle":{"$ref":"#/components/schemas/BundleExport"},"nom_cible":{"description":"Nom affiché si l'organisation doit être créée.","type":["string","null"]},"passphrase":{"description":"Passphrase de déchiffrement des secrets du bundle v2 (mode\n`configuration_donnees`). Jamais journalisée ; sans elle les\nsecrets sont sautés avec une erreur explicite au rapport.","type":["string","null"]},"slug_cible":{"description":"Slug de l'organisation cible (existante ou à créer).","type":"string"}},"required":["bundle","slug_cible"],"type":"object"},"RequeteAbstraiteReq":{"description":"Corps d'une requête abstraite à transformer.","properties":{"chemin":{"description":"Chemin demandé, commençant par /.","type":"string"},"en_tetes":{"description":"En-têtes (paires [nom, valeur]) — noms comparés sans la casse.","items":{"items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":"array"},"type":"array"},"methode":{"description":"GET | POST | PUT | DELETE… (rendue telle quelle).","type":"string"},"parametres":{"description":"Paramètres d'URL (paires [nom, valeur], ordre conservé).","items":{"items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":"array"},"type":"array"}},"required":["methode","chemin"],"type":"object"},"RequeteEnVolReq":{"properties":{"debut":{"format":"int64","minimum":0,"type":"integer"},"id":{"type":"string"}},"required":["id","debut"],"type":"object"},"RequeteEval":{"description":"La requête fictive à évaluer (dry-run et middleware).","properties":{"charge":{"description":"Charge utile à inspecter : query + corps concaténés.","type":"string"},"chemin":{"type":"string"},"entetes":{"description":"En-têtes \"K=V\".","items":{"type":"string"},"type":"array"},"ip":{"type":"string"},"methode":{"type":"string"},"taille_corps":{"description":"Taille du corps en octets.","format":"int64","minimum":0,"type":"integer"}},"required":["ip","methode","chemin","charge","taille_corps","entetes"],"type":"object"},"RequeteHistReq":{"properties":{"id":{"type":"string"},"question":{"type":"string"},"reponse":{"type":"string"}},"required":["id","question","reponse"],"type":"object"},"RequeteWhReq":{"properties":{"sql":{"description":"SELECT en lecture seule (LIMIT plafonné à 1000)","type":"string"}},"required":["sql"],"type":"object"},"Reseau":{"properties":{"baux":{"additionalProperties":{"items":false,"prefixItems":[{"type":"string"},{"format":"int64","minimum":0,"type":"integer"}],"type":"array"},"description":"Baux DHCP actifs : ip → (vm_id?, expire_epoch).","propertyNames":{"type":"string"},"type":"object"},"cidr":{"description":"CIDR IPv4 : 10.0.1.0/24","type":"string"},"created_at":{"type":"string"},"defaut_fe":{"description":"Pare-feu réseau (03/09, modèle NACL) : statut appliqué au trafic\nne correspondant à AUCUNE règle — accept (compatibilité) ou reject.","type":"string"},"dhcp":{"description":"true = serveur DHCP actif sur `plage_dhcp`.","type":"boolean"},"dns":{"description":"Serveurs DNS proposés aux VMs.","items":{"type":"string"},"type":"array"},"id":{"type":"string"},"isole":{"description":"Isolation : true = le trafic ne sort pas du réseau sans règle FW.","type":"boolean"},"nom":{"type":"string"},"passerelle":{"description":"Passerelle (dans le CIDR) — optionnelle.","type":["string","null"]},"plage_dhcp":{"description":"Plage DHCP incluse : [début, fin] — vide si dhcp=false.","items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":["array","null"]},"project_id":{"type":"string"},"regles_fe":{"description":"Règles ordonnées : première correspondance gagne, sinon `defaut_fe`.","items":{"$ref":"#/components/schemas/RegleFe"},"type":"array"},"reservations":{"additionalProperties":{"type":"string"},"description":"Réservations statiques : ip → (vm_id?, motif).","propertyNames":{"type":"string"},"type":"object"},"vlan":{"description":"VLAN 802.1Q optionnel : posé en `tag=` sur l'interface des\nmachines attachées (isolation réelle sur le bridge PVE).","format":"int32","minimum":0,"type":["integer","null"]}},"required":["id","project_id","nom","cidr","dns","dhcp","reservations","baux","isole","created_at"],"type":"object"},"ReservationReq":{"properties":{"ip":{"type":"string"},"motif":{"description":"Motif (nom de VM, rôle…).","type":"string"}},"required":["ip","motif"],"type":"object"},"ResoudreReq":{"properties":{"genre":{"type":"string"},"nom":{"type":"string"}},"required":["nom"],"type":"object"},"ResoudreRouteDnsReq":{"description":"Corps d'une résolution géo/latence.","properties":{"fqdn":{"type":"string"},"mesures":{"description":"Mesures de sondes APPORTÉES (santé + RTT) — jamais déduites ici.","items":{"$ref":"#/components/schemas/MesureCibleDnsReq"},"type":"array"},"source":{"description":"IPv4 de la source demandeuse.","type":"string"}},"required":["fqdn","source","mesures"],"type":"object"},"RessourceDeclareeReq":{"description":"Une ressource déclarée du souhaité (miroir API de\nequivalents::RessourceDeclaree — « type » sérialisé comme en IaC).","properties":{"famille":{"description":"Famille API : vms, volumes, buckets…","type":"string"},"id":{"description":"Identifiant réel dans la famille (vm-…, vol-…).","type":"string"},"nom":{"description":"Nom de la ressource.","type":"string"},"taille":{"description":"Taille (Go) — 0 quand la famille n'en a pas.","format":"int64","minimum":0,"type":"integer"},"type":{"description":"Le « type » IaC (cs-small, ssd…).","type":"string"}},"required":["famille","id","nom","type"],"type":"object"},"RessourceDslReq":{"properties":{"cout_mensuel":{"description":"Attribut DSL « cout_mensuel » — numérique, seul attribut à\nadmettre > et <.","format":"double","type":"number"},"etat":{"description":"Attribut DSL « etat » (actif, archive…).","type":"string"},"tags":{"additionalProperties":{"type":"string"},"description":"Tags posés — attributs DSL tags.<cle>.","propertyNames":{"type":"string"},"type":"object"},"type":{"description":"Attribut DSL « type » : le genre de la ressource (vm, app…).","type":"string"}},"required":["type"],"type":"object"},"RessourcePlan":{"properties":{"cible":{"description":"Équivalent Cirrostratus (cs-small, db-ha, bucket…) ou `manuel`.","type":"string"},"cout_cible":{"description":"€/h côté Cirrostratus.","format":"double","type":"number"},"cout_source":{"description":"€/h côté cloud source (estimation, 0 si inconnu).","format":"double","type":"number"},"nom_tf":{"description":"Nom Terraform.","type":"string"},"note":{"description":"Détail de la décision.","type":"string"},"source":{"description":"Type cloud source (ex. `aws_instance`).","type":"string"}},"required":["source","nom_tf","cible","note","cout_source","cout_cible"],"type":"object"},"RessourcesSuperadminVue":{"description":"Ressources vivantes de l'org dans la vue superadmin.","properties":{"instances":{"items":{"$ref":"#/components/schemas/InstanceSuperadminVue"},"type":"array"},"ipv6":{"items":{"$ref":"#/components/schemas/Ipv6SuperadminVue"},"type":"array"},"machines_proxmox":{"items":{"$ref":"#/components/schemas/MachineSuperadminVue"},"type":"array"},"serveurs_externes":{"items":{"$ref":"#/components/schemas/ServeurSuperadminVue"},"type":"array"},"vms":{"items":{"$ref":"#/components/schemas/VmSuperadminVue"},"type":"array"}},"required":["vms","machines_proxmox","instances","ipv6","serveurs_externes"],"type":"object"},"RestaurerReq":{"properties":{"destination":{"description":"Dossier de destination (créé s'il n'existe pas).","type":"string"}},"required":["destination"],"type":"object"},"RestaurerVersionReq":{"description":"Corps d'une restauration de version.","properties":{"cle":{"description":"Clé de l'objet à restaurer.","type":"string"},"id_version":{"description":"Version source (doit exister ET appartenir à cette clé).","type":"string"}},"required":["cle","id_version"],"type":"object"},"ResultatCas":{"properties":{"etat":{"type":"string"},"manques":{"items":{"type":"string"},"type":"array"},"reussi":{"type":"boolean"},"run":{"type":"string"}},"required":["reussi","manques","run","etat"],"type":"object"},"ResultatSondeLbReq":{"properties":{"adresse":{"description":"« ip:port » du backend sondé.","type":"string"},"code":{"format":"int32","minimum":0,"type":["integer","null"]},"latence_ms":{"format":"int64","minimum":0,"type":"integer"},"succes":{"type":"boolean"}},"required":["adresse","succes","latence_ms"],"type":"object"},"ResumeDonnees":{"description":"Résumé de la section données (mode `configuration_donnees`).","properties":{"buckets":{"description":"Buckets touchés par la collecte (même vides s'ils n'ont pas\nd'objet).","format":"int64","minimum":0,"type":"integer"},"objets_s3":{"description":"Objets S3 embarqués.","format":"int64","minimum":0,"type":"integer"},"octets_s3":{"description":"Somme des tailles des objets embarqués.","format":"int64","minimum":0,"type":"integer"},"secrets_chiffres":{"description":"Secrets chiffrés embarqués (0 si aucune passphrase fournie).","format":"int64","minimum":0,"type":"integer"},"tronque":{"description":"Plafond de collecte atteint (objets ou octets) : inventaire\nPARTIEL, dit comme tel.","type":"boolean"}},"required":["objets_s3","octets_s3","buckets","secrets_chiffres","tronque"],"type":"object"},"ResumeExport":{"properties":{"donnees":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ResumeDonnees","description":"Présent uniquement en mode `configuration_donnees`."}]},"familles":{"additionalProperties":{"format":"int64","minimum":0,"type":"integer"},"description":"Compteur par famille (préfixe `ref:` pour les références).","propertyNames":{"type":"string"},"type":"object"},"total_declaratif":{"description":"Ressources déclaratives embarquées (rejouables à l'import).","format":"int64","minimum":0,"type":"integer"},"total_references":{"description":"Références listées mais jamais re-créées automatiquement.","format":"int64","minimum":0,"type":"integer"}},"required":["total_declaratif","total_references","familles"],"type":"object"},"ResumerTexteReq":{"description":"La demande de résumé extractif.","properties":{"nb_phrases":{"description":"Phrases retenues (défaut 3).","minimum":0,"type":"integer"},"texte":{"type":"string"}},"required":["texte"],"type":"object"},"RetentionLogsReq":{"properties":{"incidents":{"items":{"$ref":"#/components/schemas/IncidentSerde"},"type":"array"},"politiques":{"items":{"$ref":"#/components/schemas/PolitiqueRetentionSerde"},"type":"array"}},"required":["politiques"],"type":"object"},"RetentionVideoReq":{"properties":{"jours":{"description":"Jours de rétention (1-30) — stocké en heures dans le flux.","format":"int32","minimum":0,"type":"integer"}},"required":["jours"],"type":"object"},"RetirerLabelsCarteReq":{"properties":{"nom":{"type":"string"}},"required":["nom"],"type":"object"},"RetirerTagReq":{"properties":{"cle":{"type":"string"},"ressource":{"type":"string"}},"required":["ressource","cle"],"type":"object"},"Revision":{"description":"Révision d'app (style Knative) : une image + une part du trafic.","properties":{"cree_le":{"type":"string"},"id":{"type":"string"},"image":{"type":"string"},"trafic":{"description":"Part du trafic en pourcentage (0-100).","format":"int32","minimum":0,"type":"integer"}},"required":["id","image","trafic","cree_le"],"type":"object"},"RightSizingReq":{"properties":{"cpu_pct_max":{"description":"Pointe CPU mesurée (% — 100 % = 1 vCPU).","format":"double","type":"number"},"disque_go":{"description":"Besoin disque (Go).","format":"int64","minimum":0,"type":"integer"},"gpu":{"description":"Vrai pour exiger un profil GPU.","type":"boolean"},"ram_mo_max":{"description":"Besoin RAM maximal (Mo).","format":"int64","minimum":0,"type":"integer"}},"required":["cpu_pct_max","ram_mo_max","disque_go"],"type":"object"},"RoleCustomReq":{"properties":{"nom":{"description":"Nom slug du rôle (2-40, minuscules/chiffres/tirets).","type":"string"},"permissions":{"items":{"$ref":"#/components/schemas/PermissionReq"},"type":"array"}},"required":["nom","permissions"],"type":"object"},"RollbackEdgeReq":{"properties":{"ack":{"$ref":"#/components/schemas/AckEchoueEdge","description":"L'ACK d'échec MESURÉ qui motive le rollback."}},"required":["ack"],"type":"object"},"RotationResume":{"description":"Politique de rotation jointe à chaque secret. Sans politique :\n`{\"jours\": 0, \"prochaine\": \"désactivée\"}` (derniere/due absents).","properties":{"derniere":{"description":"Dernière rotation effective (absent si jamais tourné).","type":["string","null"]},"due":{"description":"Rotation en retard (absent si désactivée).","type":["boolean","null"]},"jours":{"description":"Jours entre deux rotations ; 0 = désactivée.","format":"int32","minimum":0,"type":"integer"},"prochaine":{"description":"Prochaine échéance, ou « désactivée ».","type":"string"}},"required":["jours","prochaine"],"type":"object"},"Route":{"description":"Une route : destination (CIDR) → passerelle (IPv4).","properties":{"destination":{"description":"CIDR destination (0.0.0.0/0 = route par défaut).","type":"string"},"motif":{"description":"Pourquoi cette route (audit).","type":"string"},"passerelle":{"description":"Passerelle IPv4 — DOIT être joignable depuis le sous-réseau.","type":"string"}},"required":["destination","passerelle"],"type":"object"},"RunAgentReq":{"properties":{"question":{"type":"string"}},"required":["question"],"type":"object"},"RuntimeConteneurReq":{"description":"Corps de pose d'un runtime conteneurisé.","properties":{"handler":{"description":"Point d'entrée dans l'image (ex. main.handler).","type":"string"},"image":{"description":"Image ÉTIQUETÉE (ex. fn:1.2 — « :latest » flottant refusé).","type":"string"}},"required":["image","handler"],"type":"object"},"SanteMembre":{"description":"Santé MESURÉE d'un membre du groupe — structure SÉPARÉE de\n`VmGroupe` (le registre des groupes reste inchangé), jointe par\n`vm_id`. `etat` : sain | de_faillant | inconnu (tout autre état\nest traité « inconnu » — on ne remplace pas sur de l'indéterminé).","properties":{"etat":{"type":"string"},"vm_id":{"type":"string"}},"required":["vm_id","etat"],"type":"object"},"SanteRep":{"description":"GET /healthz — 200 « ok », ou 503 « degrade » (base de contrôle\ntombée : c'est CE code que le proxy de bascule lit, P7-04). Les\nchamps d'explication n'existent que sur le 503.","properties":{"erreur":{"description":"503 uniquement : dernière erreur constatée par le scheduler.","type":["string","null"]},"status":{"description":"ok | degrade","type":"string"},"store":{"description":"503 uniquement : « indisponible ».","type":["string","null"]},"verifie_le":{"description":"503 uniquement : horodatage de la dernière vérification.","type":["string","null"]}},"required":["status"],"type":"object"},"SanteSiteReq":{"description":"Santé MESURÉE d'un site (miroir ToSchema de\n[`crate::bascule_dns::SanteSite`]).","properties":{"en_ligne":{"description":"Hosts actifs joignables (un seul suffit à dire le site sain).","minimum":0,"type":"integer"},"hosts_actifs":{"description":"Hosts actifs enregistrés sur ce site.","minimum":0,"type":"integer"},"site":{"type":"string"}},"required":["site","hosts_actifs","en_ligne"],"type":"object"},"SantesReq":{"description":"Corps du verdict de santé des membres du groupe.","properties":{"santes":{"items":{"$ref":"#/components/schemas/SanteMembre"},"type":"array"}},"required":["santes"],"type":"object"},"ScalePoolReq":{"properties":{"nb":{"description":"Nombre de nœuds voulu — doit rester dans [nb_min, nb_max]","format":"int64","type":"integer"}},"required":["nb"],"type":"object"},"ScanReq":{"properties":{"image":{"description":"dépôt:tag du registre (ex. test/alpine:latest)","type":"string"}},"required":["image"],"type":"object"},"ScimDiffReq":{"properties":{"annuaire":{"items":{"$ref":"#/components/schemas/EntreeScimSerde"},"type":"array"}},"required":["annuaire"],"type":"object"},"SdpReq":{"description":"Corps de dépôt d'un SDP (offre ou réponse).","properties":{"sdp":{"description":"SDP opaque — structure minimale RFC 8866 vérifiée (v=, o=, s=, t=), jamais lue au-delà.","type":"string"}},"required":["sdp"],"type":"object"},"SecretChiffre":{"description":"Un secret de l'org, chiffré par la passphrase de l'opérateur.","properties":{"blob_chiffre":{"$ref":"#/components/schemas/BlobChiffre"},"nom":{"type":"string"}},"required":["nom","blob_chiffre"],"type":"object"},"SecretResume":{"description":"Un secret en liste (métadonnées + rotation, jamais la valeur).","properties":{"created_at":{"type":"string"},"id":{"type":"string"},"nom":{"type":"string"},"rotation":{"$ref":"#/components/schemas/RotationResume"},"updated_at":{"type":["string","null"]},"version":{"format":"int64","type":"integer"}},"required":["id","nom","version","created_at","rotation"],"type":"object"},"SecretsListe":{"description":"GET /v1/projects/{pid}/secrets — secrets de l'org, valeurs JAMAIS\nrendues en liste (EX-SEC-002).","properties":{"items":{"items":{"$ref":"#/components/schemas/SecretResume"},"type":"array"},"total":{"minimum":0,"type":"integer"}},"required":["items","total"],"type":"object"},"SerieDeclareeReq":{"description":"Série déclarée pour un dashboard.","properties":{"cle":{"description":"Nom de la série (ex. cpu_utilisation).","type":"string"},"nom":{"description":"Titre d'affichage du panneau.","type":"string"},"unite":{"description":"Unité rendue sur l'axe (%, ms, octets…).","type":"string"}},"required":["cle","nom","unite"],"type":"object"},"ServeurSuperadminVue":{"description":"Serveur externe (SSH) dans la vue superadmin.","properties":{"etat":{"type":"string"},"hote":{"type":"string"},"nom":{"type":"string"},"os":{"type":"string"},"port":{"format":"int32","minimum":0,"type":"integer"}},"required":["nom","hote","port","etat","os"],"type":"object"},"ServiceCompteVue":{"description":"Un service compté dans la vue d'ensemble.","properties":{"nom":{"description":"Libellé lisible (ex. « Machines virtuelles »).","type":"string"},"tab":{"description":"Onglet WebUI : vms, pmx, k8s, apps, ws, fnc, asg, inst, vol,\ns3, nfs, depot, nets, lb, dns, topics, flux, wf, mdl, athena,\ncert, budgets, res, webhooks, rpt.","type":"string"},"total":{"description":"Nombre d'éléments RÉELS au registre (0 si absent).","minimum":0,"type":"integer"}},"required":["tab","nom","total"],"type":"object"},"SeuilRapportReq":{"properties":{"colonne":{"description":"Colonne mesurée du rapport (ex. « cout_h »).","type":"string"},"comparaison":{"description":"\">\" (alerte si valeur > seuil) ou \"<\" (l'inverse).","type":"string"},"seuil":{"description":"Seuil : un nombre fini (NaN/infinité refusés).","format":"double","type":"number"}},"required":["colonne","seuil","comparaison"],"type":"object"},"SignerVersionReq":{"properties":{"artefacts":{"description":"Artefacts de la version avec leurs empreintes MESURÉES —\nl'API n'en devine aucune.","items":{"$ref":"#/components/schemas/ArtefactSignatureReq"},"type":"array"}},"required":["artefacts"],"type":"object"},"SimulArchReq":{"properties":{"lignes":{"description":"Lignes de l'architecture (famille, gabarit, quantité).","items":{"$ref":"#/components/schemas/LigneSimulReq"},"type":"array"},"reserve":{"description":"Ventiler avec la remise de réservation 36 mois (cirroReservations).","type":"boolean"}},"required":["lignes"],"type":"object"},"SiteMesh":{"description":"Un site du mesh : identité + préfixes qu'il annonce (les siens).","properties":{"id":{"type":"string"},"nom":{"type":"string"},"prefixes":{"description":"Préfixes ANNONCÉS par le site (CIDR IPv4).","items":{"type":"string"},"type":"array"}},"required":["id","nom","prefixes"],"type":"object"},"SiteMeshVpnReq":{"description":"Corps API d'un site du mesh VPN (miroir ToSchema).","properties":{"cle_publique":{"description":"Clé publique WireGuard du gateway (44 chars base64).","type":"string"},"endpoint":{"description":"host:port joignable par les autres sites.","type":"string"},"prefixe":{"description":"Préfixe servi PAR le site — c'est lui que les pairs autorisent.","type":"string"},"site":{"type":"string"}},"required":["site","endpoint","cle_publique","prefixe"],"type":"object"},"SiteVpnHaReq":{"description":"Corps de pose d'un site HA (exactement deux gateways).","properties":{"gateways":{"items":{"$ref":"#/components/schemas/GatewayVpnReq"},"type":"array"}},"required":["gateways"],"type":"object"},"SnapshotReq":{"properties":{"nom":{"description":"Nom du snapshot : DNS-1123 court (réservés PVE refusés)","type":"string"}},"required":["nom"],"type":"object"},"SondeActiveReq":{"properties":{"chemin":{"description":"Chemin interrogé (ex. « /sante »).","type":"string"},"intervalle_s":{"format":"int32","minimum":0,"type":"integer"},"seuil_echecs":{"format":"int32","minimum":0,"type":"integer"},"seuil_succes":{"format":"int32","minimum":0,"type":"integer"}},"required":["chemin","intervalle_s","seuil_echecs","seuil_succes"],"type":"object"},"Source":{"oneOf":[{"description":"Image d'un registre OCI (ex. registre:5000/mon-app).","properties":{"genre":{"enum":["image"],"type":"string"},"referentiel":{"description":"Référentiel dans le registre (ex. `mon-app`).","type":"string"},"registre":{"description":"Racine du registre (ex. `http://192.168.1.110:5000`).","type":"string"},"tag":{"description":"Tag à déployer (ex. `1.4.2`).","type":"string"}},"required":["registre","referentiel","tag","genre"],"type":"object"},{"description":"Dépôt git (la référence résolue est le commit sha).","properties":{"branche":{"description":"Branche suivie (informatif).","type":"string"},"genre":{"enum":["git"],"type":"string"},"url":{"description":"URL du dépôt (joignable en HTTP(S)).","type":"string"}},"required":["url","branche","genre"],"type":"object"}]},"SourceAthenaReq":{"description":"Requête d'exécution sur une source arbitraire fournie.","properties":{"contenu":{"description":"Contenu brut de la source (CSV en-tête en 1re ligne, JSON = tableau d'objets).","type":"string"},"format":{"description":"csv | json (le parquet est refusé nommément côté noyau).","type":"string"},"nom":{"description":"Nom de la table source (doit matcher le FROM du SQL).","type":"string"},"sql":{"type":"string"}},"required":["nom","format","contenu","sql"],"type":"object"},"SourceMultiReq":{"description":"Corps API d'une source multi (miroir ToSchema de\n[`crate::dataflow::SourceMulti`]).","properties":{"cle":{"description":"Colonne de JOINTURE portée par cette source.","type":"string"},"declaration":{"description":"sql : SELECT (garde cirroAthena) · http : URL http(s)://…","type":"string"},"genre":{"description":"sql | http.","type":"string"},"nom":{"description":"1-40 caractères, alphanumérique - _ . — préfixe les colonnes.","type":"string"}},"required":["nom","genre","declaration","cle"],"type":"object"},"SplitABReq":{"properties":{"identifiant":{"description":"Identifiant STABLE de l'appelant (utilisateur, session…).","type":"string"},"pourcentage_a":{"description":"Part des ids en bras A (1-99 — à 0 ou 100 on ne teste rien).","format":"int32","minimum":0,"type":"integer"}},"required":["identifiant","pourcentage_a"],"type":"object"},"SupervisionCacheReq":{"properties":{"metriques":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/MetriquesCacheDto","description":"Métriques INFO du moteur (le cas échéant) — transportées telles\nquelles, la structure ne calcule rien d'elle-même."}]},"moteur_repond":{"description":"Le moteur répond-il au PING ? MESURÉ par l'intégration (la\nplateforme n'a pas de client redis ici — elle ne le déduit pas).","type":"boolean"},"replication":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ObservationReplicationReq","description":"Une observation de réplication (retard ms mesuré, présence du\nflux) — facultative."}]}},"required":["moteur_repond"],"type":"object"},"SupprimerObjetS3Req":{"description":"Corps de DELETE /s3/{bucket}/objets : la clé de l'objet à supprimer.","properties":{"cle":{"description":"Clé de l'objet — chaîne OPAQUE qui contient souvent des `/`\n(préfixes, ex. « logs/2026/a.txt ») : c'est la clé entière, pas un\nsegment d'URL.","type":"string"}},"required":["cle"],"type":"object"},"SurchargeGroupes":{"description":"Surcharge de regroupement du portail : ordre, renommages et fusions\nde groupes — sérialisable, posée en configuration (metadata\n`portail:{slug}:surcharge`). Les titres référencés sont les titres\nD'INVENTAIRE (avant renommage) : la surcharge s'écrit contre ce que\nla génération produit réellement.","properties":{"fusionner_dans":{"description":"Fusions : les tuiles du groupe source migrent dans le groupe\ncible (le groupe source disparaît s'il se vide).","items":{"items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":"array"},"type":"array"},"ordre":{"description":"Ordre voulu : les groupes listés d'abord (ordre donné), les\nautres ensuite dans l'ordre stable par défaut.","items":{"type":"string"},"type":"array"},"renommer":{"description":"Renommages affichés : titre d'inventaire → titre voulu.","items":{"items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":"array"},"type":"array"}},"type":"object"},"Svid":{"properties":{"empreinte_cle_vm":{"description":"Empreinte SHA-256 de la clé publique de la VM (liant d'attestation).","type":"string"},"not_after":{"format":"int64","minimum":0,"type":"integer"},"not_before":{"format":"int64","minimum":0,"type":"integer"},"projet":{"type":"string"},"serial":{"description":"Numéro de série — strictement croissant par VM (rotation).","format":"int64","minimum":0,"type":"integer"},"spiffe_id":{"description":"`spiffe://cirrostratus.internal/vm/<id>`","type":"string"},"vm_id":{"type":"string"}},"required":["spiffe_id","vm_id","projet","empreinte_cle_vm","serial","not_before","not_after"],"type":"object"},"SvidAudiencesReq":{"properties":{"audiences":{"description":"Services destinataires autorisés (ex. [\"api\", \"jobs\"]) — la\nsignature couvre la liste : personne ne peut la réécrire.","items":{"type":"string"},"type":"array"}},"required":["audiences"],"type":"object"},"SvidSignee":{"properties":{"signature":{"description":"Signature Ed25519 du JSON canonique de `svid`, hex.","type":"string"},"svid":{"$ref":"#/components/schemas/Svid"}},"required":["svid","signature"],"type":"object"},"TableRoutage":{"description":"Table de routage d'un sous-réseau.","properties":{"routes":{"items":{"$ref":"#/components/schemas/Route"},"type":"array"},"sous_reseau":{"description":"CIDR du sous-réseau auquel la table s'applique.","type":"string"}},"required":["sous_reseau","routes"],"type":"object"},"TablesVisionReq":{"properties":{"mots":{"description":"Mots avec leurs coordonnées MESURÉES (sortie OCR / structurer_pdf).","items":{"$ref":"#/components/schemas/MotPdfReq"},"type":"array"},"seuil_alignement_x":{"description":"Tolérance d'alignement des colonnes en points — défaut produit\n[`crate::vision::SEUIL_ALIGNEMENT_X_DEFAUT`] (5).","format":"double","type":["number","null"]}},"required":["mots"],"type":"object"},"TemplateCourrielReq":{"description":"La demande de création d'un template.","properties":{"corps":{"description":"Gabarit du corps (texte brut, variables `{{clé}}`).","type":"string"},"nom":{"type":"string"},"sujet":{"description":"Gabarit du sujet (une ligne après rendu).","type":"string"}},"required":["nom","sujet","corps"],"type":"object"},"TerminalReq":{"properties":{"commande":{"type":"string"}},"required":["commande"],"type":"object"},"TestFeReq":{"properties":{"destination":{"type":"string"},"port":{"format":"int64","type":"integer"},"protocole":{"type":"string"},"source":{"type":"string"}},"required":["source","destination","port"],"type":"object"},"TestRegleApiReq":{"description":"Requête du test à blanc — TRANCHE 6 (W6-5) : `attributs` étend la\nsimulation aux règles à FILTRE RICHE (évaluées par\n[`crate::regles::correspond_riche`] sur les attributs fournis).","properties":{"attributs":{"description":"Attributs de l'événement simulé (objet JSON) — évalués par les\nfiltres riches des règles qui en portent un."},"genre":{"type":"string"},"titre":{"type":"string"}},"required":["genre"],"type":"object"},"TestRegleReq":{"description":"Simulation à blanc : cet événement déclencherait quelles règles ?","properties":{"genre":{"type":"string"},"titre":{"type":"string"}},"required":["genre"],"type":"object"},"ThemePortail":{"description":"Thème du portail : logo et couleurs injectés dans la page générée —\nsérialisable, posé en configuration (metadata `portail:{slug}:theme`).\nChaque champ est optionnel : le thème ne surcharge QUE ce qu'il pose,\nle reste de la charte « Nuit & Halo » reste.","properties":{"couleur_fond":{"description":"Couleur de fond de page — #RRGGBB.","type":["string","null"]},"couleur_primaire":{"description":"Couleur primaire (liens, focus) — #RRGGBB.","type":["string","null"]},"logo_url":{"description":"URL (http(s) ou data:) d'un logo affiché en tête.","type":["string","null"]},"titre":{"description":"Titre personnalisé de la page (défaut : « portail des services »).","type":["string","null"]}},"type":"object"},"TickPreemptionReq":{"properties":{"vivant":{"description":"Le processus existe-t-il ENCORE (mesuré par l'appelant) ?","type":"boolean"}},"required":["vivant"],"type":"object"},"TickUpgradeReq":{"description":"Corps d'un tick : l'observation MESURÉE de la dernière commande.","properties":{"heure_utc":{"description":"Heure UTC courante (0-23) — défaut : l'heure réelle du serveur.","format":"int32","minimum":0,"type":["integer","null"]},"observation":{"description":"ok | echec | version.","type":"string"},"raison":{"description":"observation « echec » : la raison telle quelle (stderr tronquée…).","type":["string","null"]},"version":{"description":"observation « version » : la version MESURÉE (None = sortie\nillisible — honnête, n'est pas « à jour »).","type":["string","null"]}},"required":["observation"],"type":"object"},"TicketsVue":{"description":"Tickets support de l'org.","properties":{"ouverts":{"description":"Ouverts ou en cours.","minimum":0,"type":"integer"},"total":{"minimum":0,"type":"integer"}},"required":["ouverts","total"],"type":"object"},"TierVolumeReq":{"properties":{"tier":{"description":"Tier visé : standard-ssd | premium-nvme.","type":"string"}},"required":["tier"],"type":"object"},"TopologieMeshReq":{"description":"Corps de pose de la topologie mesh inter-sites.","properties":{"liaisons":{"items":{"$ref":"#/components/schemas/LiaisonMesh"},"type":"array"},"sites":{"items":{"$ref":"#/components/schemas/SiteMesh"},"type":"array"}},"required":["sites","liaisons"],"type":"object"},"TopologieReq":{"description":"Corps de déclaration d'une topologie.","properties":{"primaire":{"$ref":"#/components/schemas/NoeudFerrodbReq"},"replicas":{"items":{"$ref":"#/components/schemas/NoeudFerrodbReq"},"type":"array"},"retard_max_ms":{"description":"Retard maximum exigé pour les lectures (ms, défaut 100).","format":"int64","minimum":0,"type":["integer","null"]}},"required":["primaire","replicas"],"type":"object"},"TraduireMatrixReq":{"properties":{"evenement":{"$ref":"#/components/schemas/EvenementMatrix","description":"L'événement Matrix ENTRANT (seul m.room.message/m.text se traduit)."},"reponse":{"description":"La réponse de l'agent (texte cirro) — repart en m.text.","type":"string"}},"required":["evenement","reponse"],"type":"object"},"TraduirePontReq":{"properties":{"message":{},"sequence":{"description":"Séquence de l'ordre global du topic — None (désactivé) → REFUS\nhonnête : pas de séquence, pas d'offset inventé.","format":"int64","minimum":0,"type":["integer","null"]},"sujet":{"type":"string"},"timestamp_ms":{"description":"Millisecondes depuis epoch — mesurées par l'appelant.","format":"int64","minimum":0,"type":["integer","null"]}},"required":["sujet","message"],"type":"object"},"TranscrireReq":{"properties":{"nom":{"description":"Nom du fichier uploadé (avec wav_base64).","type":["string","null"]},"voix_id":{"description":"Transcrire une synthèse cirroVoix existante (fichier déjà côté\nserveur) — prioritaire si présent.","type":["string","null"]},"wav_base64":{"description":"Contenu WAV encodé base64 (avec nom).","type":["string","null"]}},"type":"object"},"Transformation":{"description":"UNE définition de transformation, appliquée À L'ENTRAÎNEMENT ET À\nL'INFÉRENCE — la même structure, le même [`appliquer`], les mêmes\nsorties (prouvé par test : sérialisation → relecture → sorties\nidentiques, byte pour byte).","oneOf":[{"description":"La valeur telle quelle (tout type).","enum":["Identite"],"type":"string"},{"description":"(x − min) / (max − min) — PAS de rabot : une valeur hors\n[min, max] sort hors [0, 1], c'est mesuré, pas maquillé.","properties":{"Normaliser":{"description":"(x − min) / (max − min) — PAS de rabot : une valeur hors\n[min, max] sort hors [0, 1], c'est mesuré, pas maquillé.","properties":{"max":{"format":"double","type":"number"},"min":{"format":"double","type":"number"}},"required":["min","max"],"type":"object"}},"required":["Normaliser"],"type":"object"},{"description":"(x − moyenne) / écart_type.","properties":{"Standardiser":{"description":"(x − moyenne) / écart_type.","properties":{"ecart_type":{"format":"double","type":"number"},"moyenne":{"format":"double","type":"number"}},"required":["moyenne","ecart_type"],"type":"object"}},"required":["Standardiser"],"type":"object"},{"description":"ln(1 + x) — x > −1 exigé.","enum":["Log1p"],"type":"string"},{"description":"Sac à bornes croissantes : rend l'INDEX du bac (Entier) —\nnombre de bornes ≤ x. Ex. bornes [10, 20] : 5→0, 10→1, 20→2.","properties":{"Sac":{"description":"Sac à bornes croissantes : rend l'INDEX du bac (Entier) —\nnombre de bornes ≤ x. Ex. bornes [10, 20] : 5→0, 10→1, 20→2.","properties":{"bornes":{"items":{"format":"double","type":"number"},"type":"array"}},"required":["bornes"],"type":"object"}},"required":["Sac"],"type":"object"}]},"TransitionApprouveeReq":{"description":"Corps d'une transition approuvée.","properties":{"vers":{"description":"staging | prod | archive (prod exige la validation enregistrée).","type":"string"}},"required":["vers"],"type":"object"},"UpgradeAutoReq":{"description":"Corps de planification d'une montée de version automatique.","properties":{"fenetre":{"$ref":"#/components/schemas/FenetreUpgradeApiReq"},"noeuds":{"description":"Nœuds nommés avec leur rôle k3s (server-init | server-join | agent).","items":{"$ref":"#/components/schemas/NoeudUpgradeApiReq"},"type":"array"},"version_cible":{"description":"Mineure cible, forme v1.N — ÉPINGLÉE à la planification.","type":"string"}},"required":["version_cible","fenetre","noeuds"],"type":"object"},"UpgradeReq":{"properties":{"version":{"description":"Version cible — exactement une mineure au-dessus de l'actuelle","type":"string"}},"required":["version"],"type":"object"},"UsageSuperadminVue":{"description":"Usage machines (forme de la vue superadmin : RAM en Mo).","properties":{"disque_gb":{"format":"int64","type":"integer"},"ram_mb":{"format":"int64","type":"integer"},"vcpu":{"format":"int64","type":"integer"}},"required":["vcpu","ram_mb","disque_gb"],"type":"object"},"ValeurFeature":{"oneOf":[{"description":"Les entiers restent entiers (35 se resérialise 35, pas 35.0).","format":"int64","type":"integer"},{"format":"double","type":"number"},{"type":"string"},{"type":"boolean"}]},"ValidationHumaineReq":{"description":"Corps d'enregistrement d'une validation humaine.","properties":{"commentaire":{"description":"Pourquoi cette version mérite la prod.","type":"string"},"le":{"description":"Quand (RFC 3339) — l'horodatage d'AUDIT, pas un now() caché.","type":"string"},"valide_par":{"description":"Acteur qui a validé (email ou identifiant d'audit).","type":"string"}},"required":["valide_par","le","commentaire"],"type":"object"},"ValiderReservationReq":{"properties":{"ip":{"description":"Adresse IPv4 dont on veut vérifier la réservation.","type":"string"},"reseau":{"description":"Identifiant du réseau (r-…).","type":"string"}},"required":["reseau","ip"],"type":"object"},"ValiderTransformationReq":{"properties":{"echantillons":{"description":"Échantillons pour PROUVER l'application (optionnel).","items":{"$ref":"#/components/schemas/ValeurFeature"},"type":"array"},"transformation":{"$ref":"#/components/schemas/Transformation"}},"required":["transformation"],"type":"object"},"VerdictABReq":{"properties":{"bras_a":{"$ref":"#/components/schemas/CompteursABApi","description":"Compteurs MESURÉS du bras A."},"bras_b":{"$ref":"#/components/schemas/CompteursABApi","description":"Compteurs MESURÉS du bras B."},"delta_min_ms":{"description":"Plancher d'écart pour nommer un gagnant (ms, ≥ 0).","format":"int64","type":"integer"}},"required":["bras_a","bras_b","delta_min_ms"],"type":"object"},"VerdictAffinageReq":{"description":"La demande de verdict d'un affinage.","properties":{"execution":{"description":"Exécution d'éval qui tranche (`exe-…`, dans les suites d'éval\ndes agents de l'org).","type":"string"},"seuil_pct":{"description":"Seuil de score % exigé pour un Go (défaut 80).","format":"double","type":"number"}},"required":["execution"],"type":"object"},"VerdictLimitesReq":{"properties":{"duree_ms":{"format":"int64","minimum":0,"type":"integer"},"memoire_mo":{"description":"Pic mémoire MESURÉ en Mo (None = non lu : jamais coupée sur elle).","format":"int64","minimum":0,"type":["integer","null"]}},"required":["duree_ms"],"type":"object"},"VerdictSanteReq":{"properties":{"nom":{"description":"Nom du service DANS la zone (nom relatif sanitisé).","type":"string"},"succes":{"description":"Verdict MESURÉ de la sonde.","type":"boolean"}},"required":["nom","succes"],"type":"object"},"VerifierJetonAppReq":{"properties":{"jeton":{"$ref":"#/components/schemas/JetonApp"}},"required":["jeton"],"type":"object"},"VerifierLotReq":{"properties":{"graine":{"type":"string"},"n":{"minimum":0,"type":"integer"},"prefixe":{"type":"string"}},"required":["prefixe","n","graine"],"type":"object"},"VerifierMiroirReq":{"properties":{"empreinte_miroir":{"description":"Empreinte sha256 MESURÉE côté miroir.","type":["string","null"]},"empreinte_source":{"description":"Empreinte sha256 MESURÉE côté forge (sortie des commandes du plan).","type":["string","null"]}},"type":"object"},"VersionEdge":{"description":"Une version d'artefact livrée à un device (composant + numéro).","properties":{"composant":{"type":"string"},"fichiers":{"items":{"$ref":"#/components/schemas/FichierEdge"},"type":"array"},"version":{"format":"int32","minimum":0,"type":"integer"}},"required":["composant","version","fichiers"],"type":"object"},"VersionObjetApiReq":{"description":"Miroir API d'une version d'objet (noyau sans ToSchema).","properties":{"cle":{"type":"string"},"courante":{"type":"boolean"},"date":{"description":"RFC 3339 de la création de cette version.","type":"string"},"empreinte":{"description":"Empreinte SHA-256 du contenu (identité de la version).","type":"string"},"id_version":{"type":"string"},"taille_octets":{"format":"int64","type":"integer"}},"required":["cle","id_version","taille_octets","empreinte","date","courante"],"type":"object"},"VersionsReq":{"description":"Corps de déclaration de versions d'objets.","properties":{"versions":{"description":"Versions MESURÉES côté stockage (id, taille, empreinte, date).","items":{"$ref":"#/components/schemas/VersionObjetApiReq"},"type":"array"}},"required":["versions"],"type":"object"},"VmAMigrerApiReq":{"description":"Miroir API d'une VM à migrer (charges MESURÉES).","properties":{"charge_cpu_pct":{"description":"Charge CPU % MESURÉE (None = non mesurée).","format":"double","type":["number","null"]},"duree_migration_s":{"description":"Durée de sa dernière migration MESURÉE (s) — exigée.","format":"int64","minimum":0,"type":["integer","null"]},"vm":{"type":"string"}},"required":["vm"],"type":"object"},"VmActionReq":{"properties":{"action":{"type":"string"}},"required":["action"],"type":"object"},"VmSuperadminVue":{"description":"VM déclarative (Lot 1) dans la vue superadmin.","properties":{"etat":{"type":"string"},"ip":{"description":"null tant que la VM n'a pas d'IP.","type":["string","null"]},"nom":{"type":"string"},"type":{"description":"Genre de VM déclarative.","type":"string"}},"required":["nom","type","etat"],"type":"object"},"VueEnsembleResume":{"description":"GET /v1/projects/{pid}/vue-ensemble — première page d'une\norganisation : compteurs RÉELS de chaque service, budget, coûts.","properties":{"budget":{"$ref":"#/components/schemas/BudgetUsageVue","description":"Budget machines dérivé des quotas de l'org."},"cout_mois":{"description":"Coût du mois : somme des lignes de facture (€, arrondi au centime).","format":"double","type":"number"},"cout_ressources":{"$ref":"#/components/schemas/CoutRessources","description":"Coût mensuel PAR RESSOURCE vivante, aux tarifs exacts de la\nfacture — c'est ce total que la WebUI affiche comme coût du mois."},"org":{"$ref":"#/components/schemas/OrgVue"},"services":{"description":"Compteurs réels de chaque service (un service absent vaut 0).","items":{"$ref":"#/components/schemas/ServiceCompteVue"},"type":"array"},"tickets":{"$ref":"#/components/schemas/TicketsVue"},"usage":{"$ref":"#/components/schemas/BudgetUsageVue","description":"Usage réel des machines de l'org (mêmes unités que le budget)."}},"required":["org","budget","usage","cout_mois","cout_ressources","tickets","services"],"type":"object"},"VueEnsembleSuperadmin":{"description":"GET /v1/superadmin/vue-ensemble — toutes les organisations avec\nquotas, usage, ressources et coût du mois.","properties":{"demandes_quota":{"description":"Demandes de quota en attente (forme du registre, org remplie).","items":{},"type":"array"},"organisations":{"items":{"$ref":"#/components/schemas/OrgSuperadminVue"},"type":"array"},"plages_ipv6":{"description":"Plages IPv6 : sérialisation de la plage du registre + champ\n`allouees` (nombre d'IP prises, comptées en direct).","items":{},"type":"array"},"total_orgs":{"minimum":0,"type":"integer"}},"required":["organisations","total_orgs","plages_ipv6","demandes_quota"],"type":"object"},"WebhookAgentReq":{"description":"La demande de création d'un déclencheur.","properties":{"agent":{"description":"Agent déclenché (`ag-…`).","type":"string"}},"required":["agent"],"type":"object"},"WhoamiRep":{"description":"GET /v1/whoami — identité et adhésions du porteur du jeton.","properties":{"adhesions":{"description":"Adhésions (org + rôle) du compte.","items":{"$ref":"#/components/schemas/Adhesion"},"type":"array"},"admin_plateforme":{"type":"boolean"},"email":{"type":"string"},"id":{"type":"string"},"mfa_active":{"type":"boolean"},"superadmin":{"description":"Membre de la liste meta `superadmins` (ou admin plateforme).","type":"boolean"}},"required":["id","email","admin_plateforme","superadmin","mfa_active","adhesions"],"type":"object"},"YoloReq":{"description":"Corps d'une détection YOLO.","properties":{"hauteur_image":{"description":"Hauteur de l'image en pixels.","format":"int32","minimum":0,"type":"integer"},"largeur_image":{"description":"Largeur de l'image en pixels (conversion des coordonnées).","format":"int32","minimum":0,"type":"integer"},"seuil_confiance":{"description":"Seuil de confiance [0,1] — sous le seuil : écarté, compté.","format":"float","type":"number"},"seuil_iou":{"description":"Seuil IoU ]0,1] du NMS (0 supprimerait tout recouvrement).","format":"float","type":"number"},"sortie":{"description":"Sortie brute du détecteur : une détection par ligne, six champs (classe confiance x y l h — coordonnées normalisées).","type":"string"}},"required":["sortie","largeur_image","hauteur_image","seuil_confiance","seuil_iou"],"type":"object"},"ZoneInverseReq":{"description":"La demande de pose d'une zone inverse.","properties":{"domaine":{"description":"Domaine des FQDN servis (ex. « lab.internal »).","type":"string"},"reseau":{"description":"Identifiant du réseau (net-…) dont le CIDR devient la zone.","type":"string"},"ttl":{"description":"TTL des PTR posés (1-86 400 s, défaut 3 600).","format":"int32","minimum":0,"type":"integer"}},"required":["reseau","domaine"],"type":"object"}}},"info":{"description":"Le meilleur de chaque nuage, dans le vôtre. Lot 0 : auth, IAM multi-organisations, clés API, MFA, audit chaîné.","license":{"identifier":"Apache-2.0","name":"Apache-2.0"},"title":"Cirrostratus — API du control plane","version":"0.132.0"},"openapi":"3.1.0","paths":{"/":{"get":{"operationId":"accueil","responses":{"200":{"description":"OK"}},"tags":["systeme"]}},"/favicon.ico":{"get":{"operationId":"favicon","responses":{"200":{"description":"Icône du site (image/x-icon)"}},"summary":"Icône du site (nuage sur fond bleu nuit, générée par PIL).","tags":["systeme"]}},"/healthz":{"get":{"operationId":"sante","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SanteRep"}}},"description":"OK — base de contrôle joignable"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SanteRep"}}},"description":"DÉGRADÉ — base de contrôle indisponible (le proxy de bascule P7-04 réagit à ce code)"}},"tags":["systeme"]}},"/healthz/complet":{"get":{"operationId":"sante_complete","responses":{"200":{"description":"État des composants (≈ AWS status checks) : base, registre, driver, Proxmox, instances"},"503":{"description":"DÉGRADÉ — au moins un composant de base est indisponible (store ou registre) ; le corps rend le détail par composant"}},"tags":["systeme"]}},"/metrics":{"get":{"operationId":"metrics_prometheus","responses":{"200":{"description":"Exposition Prometheus (texte)"}},"summary":"Métriques Prometheus (P5-08) — endpoint public (scrapeur).","tags":["systeme"]}},"/openapi.json":{"get":{"operationId":"openapi_racine","responses":{"200":{"description":"Contrat OpenAPI 3.1 complet (alias racine de /v1/openapi.json)"}},"summary":"FIX-SEC-003 : alias racine de la spec. Les outils d'exploration ont\nl'habitude de demander `/openapi.json` à la racine — la 404 résultante\nnourrissait le compteur de scan du pare-feu (une découverte légitime\nfaisait grimper la réputation de l'IP). La racine sert EXACTEMENT la\nmême spec que /v1/openapi.json (pas de redirection : les clients qui\nne suivent pas les 302 reçoivent le document du premier coup).","tags":["systeme"]}},"/swagger":{"get":{"operationId":"page_swagger","responses":{"200":{"description":"Swagger UI (swagger-ui-dist Apache-2.0 embarqué, lit /v1/openapi.json)"}},"tags":["systeme"]}},"/swagger/swagger-ui-bundle.js":{"get":{"operationId":"asset_swagger_js","responses":{"200":{"description":"Script swagger-ui (embarqué)"}},"tags":["systeme"]}},"/swagger/swagger-ui.css":{"get":{"operationId":"asset_swagger_css","responses":{"200":{"description":"Feuille de style swagger-ui (embarquée)"}},"tags":["systeme"]}},"/v1/academie/verification/{serie}":{"get":{"operationId":"verification_certificat","parameters":[{"in":"path","name":"serie","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Authenticité d'un certificat — PUBLIC, sans authentification"}},"tags":["systeme"]}},"/v1/agents/webhooks/{wid}":{"post":{"operationId":"demarrer_run_webhook_agent","parameters":[{"description":"identifiant du déclencheur (whk-…)","in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemandeRunWebhookReq"}}},"required":true},"responses":{"200":{"description":"Run exécuté (même boucle ReAct que l'API) — réponse et étapes mesurées"},"401":{"description":"Clé refusée (empreinte différente)"},"403":{"description":"Déclencheur désactivé / agent incohérent"},"404":{"description":"Déclencheur inconnu"}},"tags":["general"]}},"/v1/agents/{aid}/widget":{"get":{"operationId":"widget_agent","parameters":[{"description":"Identifiant de l'agent (ag-…)","in":"path","name":"aid","required":true,"schema":{"type":"string"}},{"description":"Identifiant du déclencheur webhook (whk-…) — c'est LUI que le widget appelle","in":"query","name":"webhook","required":true,"schema":{"type":"string"}},{"description":"Clé du webhook — vérifiée par empreinte SHA-256, jamais stockée","in":"query","name":"cle","required":true,"schema":{"type":"string"}},{"description":"Titre du panneau (1-60 caractères, défaut « Assistant »)","in":"query","name":"titre","required":true,"schema":{"type":"string"}},{"description":"Couleur d'accent #rrggbb (défaut #0e7490)","in":"query","name":"couleur","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Snippet HTML+JS SELF-CONTAINED (text/html) : bulle + panneau + fetch POST vers le webhook, tout INLINE, aucun CDN — preuve par parse"},"401":{"description":"CLE_REFUSEE — la clé ne correspond pas au déclencheur"},"403":{"description":"WEBHOOK_DESACTIVE"},"404":{"description":"Agent ou déclencheur inconnu"}},"tags":["agents"]}},"/v1/anomalies/{id}/quarantine":{"post":{"operationId":"quarantaine_anomalie","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"Quarantaine (NF-SEC-010) : bloque la clé à l'auth, un clic, tracé.","tags":["securite"]}},"/v1/anomalies/{id}/release":{"post":{"operationId":"lever_anomalie","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["securite"]}},"/v1/audit":{"get":{"operationId":"audit_liste","parameters":[{"description":"max 1000 (s'applique aux items rendus ; `total` compte TOUTE la portée, comme /audit/verify)","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"description":"Événements (admin plateforme : tous ; admin d'org : les siens uniquement)"}},"tags":["audit"]}},"/v1/audit/reparer":{"post":{"operationId":"audit_reparer","responses":{"200":{"description":"Chaîne re-scellée à partir du premier maillon cassé (contenu des événements inchangé) ; {\"deja_valide\": true} si rien à faire"}},"tags":["audit"]}},"/v1/audit/verify":{"get":{"operationId":"audit_verifier","responses":{"200":{"description":"Intégrité de la chaîne (admin plateforme : globale ; admin d'org : son segment)"}},"tags":["audit"]}},"/v1/auth/login":{"post":{"operationId":"login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginReq"}}},"required":true},"responses":{"200":{"description":"Jeton JWT court"}},"tags":["auth"]}},"/v1/break-glass/seal":{"post":{"operationId":"bg_sceller","responses":{"201":{"description":"Créé"}},"summary":"Génère le scellé double-clé — une seule fois, affichée une seule fois.\nRéservé à l'opérateur plateforme, au moment de l'initialisation.","tags":["securite"]}},"/v1/break-glass/{id}":{"delete":{"operationId":"bg_revoquer","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"summary":"Révocation anticipée — n'importe quel admin (même d'une autre org en\nplateforme) peut couper une session suspecte.","tags":["securite"]}},"/v1/ca":{"get":{"operationId":"etat_ca","responses":{"200":{"description":"cirroCA : certificat racine (public) et dates"}},"tags":["securite"]}},"/v1/catalogue/goldens":{"get":{"operationId":"catalogue_goldens","responses":{"200":{"description":"Images golden : rôle + base du catalogue OS + préinstallation déclarée, taille DÉRIVÉE (base + supplément) — cohérence base/rôle/unicité garantie par le noyau"}},"tags":["systeme"]}},"/v1/catalogue/gpus":{"get":{"operationId":"catalogue_gpus","responses":{"200":{"description":"Catalogue des GPU proposés — socle de la data source Terraform cirro_gpus"}},"tags":["compute"]}},"/v1/catalogue/osimages":{"get":{"operationId":"catalogue_osimages","responses":{"200":{"description":"Catalogue des images OS (lxc/vm/app) — socle de la data source Terraform cirro_osimages"}},"tags":["compute"]}},"/v1/console":{"post":{"operationId":"executer_console","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsoleRep"}}},"description":"Commande exécutée — code de sortie réel rendu honnêtement (124 = délai 10 s dépassé et processus tué, 127 = binaire introuvable)"},"400":{"description":"Commande refusée : vide, guillemet non fermé, chemin en tête, traversée ../, --serveur (protection du jeton) ou login"},"403":{"description":"Rôle project.read minimum requis sur l'organisation du contexte (ou aucune organisation pour le compte)"},"404":{"description":"Organisation du contexte introuvable"}},"tags":["console"]}},"/v1/etats":{"get":{"operationId":"dictionnaire_etats","responses":{"200":{"description":"Table du dictionnaire d'états : chaque état brut réellement émis par genre de ressource (instances, vms, apps, workspaces, k8s, node_pools, machines) avec son libellé canonique FR (demarree|arretee|en_attente|erreur|inconnu) et sa source fichier:ligne ; `libelles` documente la légende"}},"tags":["systeme"]}},"/v1/hardware/nodes":{"get":{"operationId":"inventaire_materiel","responses":{"200":{"description":"Flotte de DÉMONSTRATION du SimulationDriver (champ source) — aucun inventaire devlink/ethtool réel n'est branché"}},"tags":["materiel"]}},"/v1/iam/check":{"post":{"operationId":"iam_check","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IamCheckReq"}}},"required":true},"responses":{"200":{"description":"OK"}},"summary":"« X peut-il faire Y sur Z ? » — évalue la situation réelle du sujet\n(bindings, plateforme, break-glass) et renvoie la chaîne de décision.","tags":["iam"]}},"/v1/iam/matrix":{"get":{"operationId":"iam_matrix","responses":{"200":{"description":"OK"}},"summary":"Matrice rôle × action (source de vérité : model::peut).","tags":["iam"]}},"/v1/iam/simulate":{"post":{"operationId":"iam_simulate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IamSimulateReq"}}},"required":true},"responses":{"200":{"description":"OK"}},"summary":"Simule un binding fictif — sans rien écrire (EX-IAM-020).","tags":["iam"]}},"/v1/inscription":{"post":{"operationId":"inscrire","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InscriptionReq"}}},"required":true},"responses":{"201":{"description":"Demande enregistrée — un superadmin la valide (org et compte créés à l'approbation)"},"409":{"description":"Une demande est déjà en attente pour cet email"}},"summary":"Formulaire d'inscription PUBLIC : nom, prénom, email, téléphone, nom\nd'organisation. Ne crée RIEN tout seul — la demande attend la\nvalidation d'un superadmin (jamais d'org auto-créée).","tags":["iam"]}},"/v1/ipv6/plages":{"get":{"operationId":"lister_plages","responses":{"200":{"description":"Plages IPv6 publiques définies par le superadmin"}},"tags":["reseau"]},"post":{"operationId":"creer_plage","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewPlageReq"}}},"required":true},"responses":{"200":{"description":"Plage IPv6 créée (superadmin)"}},"tags":["reseau"]}},"/v1/ipv6/plages/{plid}":{"delete":{"operationId":"supprimer_plage","parameters":[{"in":"path","name":"plid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plage supprimée (refusée si des IPs y sont encore affectées)"}},"tags":["reseau"]}},"/v1/marketplace":{"get":{"operationId":"catalogue_marketplace","responses":{"200":{"description":"OK"}},"tags":["llm"]},"post":{"operationId":"installer_marketplace","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallMarketplaceReq"}}},"required":true},"responses":{"201":{"description":"Créé"}},"tags":["llm"]}},"/v1/me/api-keys":{"get":{"operationId":"lister_cles","responses":{"200":{"description":"Clés"}},"tags":["cles"]},"post":{"operationId":"creer_cle","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewApiKeyReq"}}},"required":true},"responses":{"201":{"description":"Clé créée — secret affiché une seule fois"}},"tags":["cles"]}},"/v1/me/api-keys/{id}":{"delete":{"operationId":"revoquer_cle","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Clé révoquée"}},"tags":["cles"]}},"/v1/me/mfa/enable":{"post":{"operationId":"mfa_enable","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaEnableReq"}}},"required":true},"responses":{"200":{"description":"MFA activé"}},"tags":["auth"]}},"/v1/me/mfa/setup":{"post":{"operationId":"mfa_setup","responses":{"200":{"description":"Secret TOTP + URI otpauth"}},"tags":["auth"]}},"/v1/me/password":{"post":{"operationId":"changer_mdp","requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"204":{"description":"Mot de passe changé — aucun corps (le code réellement servi)"}},"summary":"Changement de mot de passe — lève les obligations break-glass en attente.","tags":["systeme"]}},"/v1/migrate/plan":{"post":{"operationId":"plan_migration","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrationReq"}}},"required":true},"responses":{"200":{"description":"Plan ressource à ressource + coûts"}},"summary":"Plan de migration depuis un Terraform AWS/GCP/Azure (P5-12).","tags":["migration"]}},"/v1/openapi.json":{"get":{"operationId":"openapi","responses":{"200":{"description":"Contrat OpenAPI 3.1 complet"}},"tags":["systeme"]}},"/v1/organizations":{"get":{"operationId":"lister_orgs","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgsListe"}}},"description":"Organisations visibles (toutes pour un admin plateforme, les siennes sinon)"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Jeton absent/invalide"}},"tags":["iam"]},"post":{"operationId":"creer_org","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewOrgReq"}}},"required":true},"responses":{"201":{"description":"Organisation créée"}},"tags":["iam"]}},"/v1/organizations/{oid}/break-glass":{"get":{"operationId":"bg_lister","parameters":[{"in":"path","name":"oid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"Liste les sessions (expiry paresseuse appliquée au passage).","tags":["securite"]},"post":{"operationId":"bg_demarrer","parameters":[{"in":"path","name":"oid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewSessionBgReq"}}},"required":true},"responses":{"201":{"description":"Session d'urgence ouverte (le code réellement servi)"}},"summary":"Ouvre une session d'urgence : les DEUX moitiés + une raison.\nAlerte tous les admins via l'audit (bg.alerte) — poussée temps réel au\nlot observabilité.","tags":["securite"]}},"/v1/organizations/{org}":{"get":{"operationId":"detail_org","parameters":[{"description":"slug ou id","in":"path","name":"org","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail + membres"}},"tags":["iam"]}},"/v1/organizations/{org}/members":{"get":{"operationId":"lister_membres","parameters":[{"in":"path","name":"org","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Membres — created_at au format RFC3339 uniforme (secondes, Z)"}},"tags":["iam"]},"post":{"operationId":"ajouter_membre","parameters":[{"in":"path","name":"org","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewMemberReq"}}},"required":true},"responses":{"201":{"description":"Membre ajouté"}},"tags":["iam"]}},"/v1/organizations/{org}/members/{email}":{"delete":{"operationId":"retirer_membre","parameters":[{"in":"path","name":"org","required":true,"schema":{"type":"string"}},{"description":"Email du membre (encodez « @ » en %40 dans le chemin) ou son identifiant d'utilisateur usr-…","in":"path","name":"email","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Membre retiré"}},"tags":["iam"]}},"/v1/organizations/{org}/membres":{"get":{"operationId":"lister_membres_fr","parameters":[{"in":"path","name":"org","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Membres (alias français de /members)"}},"tags":["iam"]},"post":{"operationId":"ajouter_membre_fr","parameters":[{"in":"path","name":"org","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewMemberReq"}}},"required":true},"responses":{"201":{"description":"Membre ajouté (alias français de /members)"}},"tags":["iam"]}},"/v1/organizations/{org}/membres/{email}":{"delete":{"operationId":"retirer_membre_fr","parameters":[{"in":"path","name":"org","required":true,"schema":{"type":"string"}},{"description":"Email du membre (encodez « @ » en %40 dans le chemin) ou son identifiant d'utilisateur usr-…","in":"path","name":"email","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Membre retiré (alias français de /members)"}},"tags":["iam"]}},"/v1/organizations/{org}/projects":{"get":{"operationId":"lister_projets","parameters":[{"description":"Slug de l'organisation","in":"path","name":"org","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Projets (groupements logiques : les ids prj-* ne servent à cibler aucune route — les ressources se gèrent au niveau de l'org via /v1/projects/{slug-org}/…)"}},"tags":["iam"]},"post":{"operationId":"creer_projet","parameters":[{"description":"Slug de l'organisation","in":"path","name":"org","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewProjectReq"}}},"required":true},"responses":{"201":{"description":"Projet créé — groupement logique uniquement : les ressources se créent au niveau de l'org (les routes /v1/projects/{pid}/… prennent le slug d'org, jamais un id prj-*)"}},"tags":["iam"]}},"/v1/organizations/{org}/projects/{pid}":{"delete":{"operationId":"supprimer_projet","parameters":[{"description":"Slug de l'organisation","in":"path","name":"org","required":true,"schema":{"type":"string"}},{"description":"Id prj-* ou slug du projet à supprimer","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Projet supprimé (groupement logique — les ressources de l'org ne sont pas affectées)"},"404":{"description":"Projet introuvable dans cette organisation"}},"summary":"B6 (audit 15/09) : les projets sont des groupements logiques —\naucun id prj-* n'est ciblable par les routes ressources. On peut\ndonc en supprimer un sans toucher aux ressources (elles vivent\nau niveau de l'org).","tags":["iam"]}},"/v1/projects/{pid}/academie/certificats":{"get":{"operationId":"mes_certificats_academie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"VOS certificats (email du jeton) + progression par cursus"}},"tags":["general"]}},"/v1/projects/{pid}/academie/certificats/{cid}/image":{"get":{"operationId":"image_certificat_academie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Le certificat illustré — ?format=svg (défaut) ou png (haute définition)"}},"tags":["general"]}},"/v1/projects/{pid}/academie/cours":{"get":{"operationId":"lister_cours_academie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Catalogue des cours (filtre ?cursus=optionnel)"}},"tags":["general"]}},"/v1/projects/{pid}/academie/cours/{cid}":{"get":{"operationId":"detail_cours_academie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Cours complet — markdown embarqué, services couverts"}},"tags":["general"]}},"/v1/projects/{pid}/academie/cours/{cid}/quiz":{"get":{"operationId":"quiz_cours_academie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Questions du quiz d'entraînement — SANS les réponses"}},"tags":["general"]},"post":{"operationId":"repondre_quiz_academie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReponsesQuizReq"}}},"required":true},"responses":{"200":{"description":"Quiz noté : score, corrections AVEC explications (l'entraînement explique)"}},"tags":["general"]}},"/v1/projects/{pid}/academie/examen/debut":{"post":{"operationId":"debuter_examen_academie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DebutExamenReq"}}},"required":true},"responses":{"201":{"description":"Examen tiré (jeton à usage unique, expiration incluse) — questions SANS réponses"}},"tags":["general"]}},"/v1/projects/{pid}/academie/examen/{jeton}/rendre":{"post":{"operationId":"rendre_examen_academie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"jeton","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReponsesQuizReq"}}},"required":true},"responses":{"200":{"description":"Copie notée + corrections ; certificat émis si le seuil est atteint"}},"tags":["general"]}},"/v1/projects/{pid}/academie/parcours-avances":{"get":{"operationId":"parcours_avances_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Cours du parcours avancé dans l'ordre conseillé (les nouvelles banques de questions sont servies par le quiz existant)"}},"tags":["general"]}},"/v1/projects/{pid}/academie/recertifications":{"get":{"operationId":"recertifications_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"true : uniquement les recertifications dues (à repasser/échues/révoquées)","in":"query","name":"dues_seules","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Statuts MESURÉS depuis les délivrances réelles (échéance = délivrance + 365 j, fenêtre de rappel 30 j — une date illisible ne prouve rien : à repasser"}},"tags":["general"]}},"/v1/projects/{pid}/accompagnement":{"get":{"operationId":"accompagnement_accueillir","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Chatbot d'accueil : salutation personnalisée + parcours de démarrage en 6 étapes + prochaine étape dérivée des compteurs RÉELS de l'org + définition de l'agent guide prêt à poser (POST ../accompagnement/agent)"}},"tags":["agents"]}},"/v1/projects/{pid}/accompagnement/agent":{"post":{"operationId":"pose_agent_guide","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Agent guide posé + déclencheur webhook créé : la clé en clair n'est JAMAIS re-rendue après cette réponse, et l'URL du widget embarquable est rendue"},"409":{"description":"DEJA_POSE (l'accompagnateur existe déjà, son id est rendu) ou AUCUN_MOTEUR_ACTIF (aucun endpoint LLM actif — le parcours marche, l'agent non)"}},"tags":["agents"]}},"/v1/projects/{pid}/accompagnement/etape/{n}":{"get":{"operationId":"etape_accompagnement","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Numéro d'étape 1..=6","in":"path","name":"n","required":true,"schema":{"format":"int32","minimum":0,"type":"integer"}}],"responses":{"200":{"description":"Contenu de l'étape du parcours : objectif, commandes CLI réelles, endpoint API, preuve attendue"},"400":{"description":"ETAPE_INCONNUE — n hors 1..=6, refus nommé"}},"tags":["agents"]}},"/v1/projects/{pid}/affinages":{"get":{"operationId":"lister_affinages","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Travaux d'affinage de l'org (état du moteur inclus)"}},"tags":["general"]}},"/v1/projects/{pid}/affinages/{fid}":{"get":{"operationId":"detail_affinage","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Travail d'affinage + journal epoch par epoch"}},"tags":["general"]}},"/v1/projects/{pid}/affinages/{fid}/verdict":{"post":{"operationId":"verdict_affinage_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerdictAffinageReq"}}},"required":true},"responses":{"200":{"description":"Verdict go|nogo, raisons citant les mesures (score, pertes, cas manqués)"},"404":{"description":"Affinage ou exécution d'éval inconnu"}},"tags":["llm"]}},"/v1/projects/{pid}/agents":{"get":{"operationId":"lister_agents","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Agents de l'org + état honnête du moteur LLM"}},"tags":["general"]},"post":{"operationId":"creer_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewAgentReq"}}},"required":true},"responses":{"201":{"description":"Agent créé (scaffold : instructions + outils + politique)"}},"tags":["general"]}},"/v1/projects/{pid}/agents/evals":{"get":{"operationId":"lister_evals_agents","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Suites d'évaluation des agents"}},"tags":["general"]},"post":{"operationId":"creer_eval_agents","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewEvalReq"}}},"required":true},"responses":{"201":{"description":"Suite d'évaluation créée (cas + critères)"}},"tags":["general"]}},"/v1/projects/{pid}/agents/evals/{eid}":{"get":{"operationId":"detail_eval_agents","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"eid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Suite d'évaluation + exécutions notées"}},"tags":["general"]}},"/v1/projects/{pid}/agents/evals/{eid}/executer":{"post":{"operationId":"executer_eval_agents","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"eid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Un run réel par cas, notation mécanique, score %"}},"tags":["general"]}},"/v1/projects/{pid}/agents/manifestes/import":{"post":{"operationId":"importer_manifeste_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImporterManifesteReq"}}},"required":true},"responses":{"201":{"description":"Agent créé depuis le manifeste VALIDÉ (schéma, outils du catalogue, politique) — les champs portés sont recopiés TELS QUELS"},"400":{"description":"Manifeste invalide"}},"tags":["agents"]}},"/v1/projects/{pid}/agents/matrix/ponts":{"get":{"operationId":"lister_ponts_matrix","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Ponts agent ↔ salon Matrix — SANS credentials (l'access token vit côté serveur du pont, preuve par scan des clés)"}},"tags":["agents"]},"post":{"operationId":"creer_pont_matrix_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewPontMatrixReq"}}},"required":true},"responses":{"201":{"description":"Pont créé (id pont-…) — salon et homeserver validés, PREUVE par scan qu'aucun credential ne s'est glissé dans le descripteur"},"400":{"description":"PONT_REFUSE — salon (!…:… ou #…:…), homeserver http(s), agent ag-…"},"404":{"description":"Agent inconnu de l'org"}},"tags":["agents"]}},"/v1/projects/{pid}/agents/matrix/ponts/{pontid}":{"delete":{"operationId":"supprimer_pont_matrix","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"pontid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pont supprimé"},"404":{"description":"Pont inconnu"}},"tags":["agents"]},"put":{"operationId":"maj_pont_matrix","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"pontid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajPontMatrixReq"}}},"required":true},"responses":{"200":{"description":"Pont mis à jour (activation, politique de réponse)"},"404":{"description":"Pont inconnu"}},"tags":["agents"]}},"/v1/projects/{pid}/agents/matrix/traduire":{"post":{"operationId":"traduire_matrix_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TraduireMatrixReq"}}},"required":true},"responses":{"200":{"description":"Aller-retour : événement Matrix → message cirro, réponse cirro → événement m.room.message/m.text — la politique du pont décide du déclenchement (mention absente = non déclenché, DIT)"},"400":{"description":"TRADUCTION_REFUSEE — autre type que m.room.message/m.text : refus nommé, on ne devine pas une image"},"404":{"description":"Aucun pont actif pour ce salon"}},"tags":["agents"]}},"/v1/projects/{pid}/agents/outils":{"get":{"operationId":"catalogue_outils_agents","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Catalogue des outils agents (107 outils réels : une vue de lecture par famille de services + actions auditées)"}},"tags":["general"]}},"/v1/projects/{pid}/agents/runs/{rid}":{"get":{"operationId":"detail_run_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail complet d'un run : étapes, outils, tokens, coût"}},"tags":["general"]}},"/v1/projects/{pid}/agents/webhooks":{"get":{"operationId":"lister_webhooks_agents","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Déclencheurs de l'org (empreintes seulement — les clés ne sont jamais rendues)"}},"tags":["general"]},"post":{"operationId":"creer_webhook_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookAgentReq"}}},"required":true},"responses":{"201":{"description":"Déclencheur créé — la clé (rendue UNE seule fois) authentifie POST /v1/agents/webhooks/{wid}"},"404":{"description":"Agent inconnu"}},"tags":["general"]}},"/v1/projects/{pid}/agents/webhooks/{wid}":{"delete":{"operationId":"supprimer_webhook_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Déclencheur supprimé — la porte est fermée"},"404":{"description":"Déclencheur inconnu"}},"tags":["general"]}},"/v1/projects/{pid}/agents/{aid}":{"delete":{"operationId":"supprimer_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Agent retiré (l'historique des runs reste lisible)"}},"tags":["general"]},"get":{"operationId":"detail_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'un agent (instructions, outils, politique, stats)"}},"tags":["general"]},"patch":{"operationId":"modifier_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchAgentReq"}}},"required":true},"responses":{"200":{"description":"Agent modifié (instructions/outils/politique/llm)"}},"tags":["general"]}},"/v1/projects/{pid}/agents/{aid}/manifeste":{"get":{"operationId":"exporter_manifeste_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Manifeste portable (instructions, outils, politique, mémoire) — dépouillé de tout ce qui est local (id, org, endpoint, dates)"}},"tags":["agents"]}},"/v1/projects/{pid}/agents/{aid}/notes":{"get":{"operationId":"lister_notes_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Mémoire persistante de l'agent"}},"tags":["general"]},"post":{"operationId":"ajouter_note_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteReq"}}},"required":true},"responses":{"201":{"description":"Note ajoutée à la mémoire de l'agent"}},"tags":["general"]}},"/v1/projects/{pid}/agents/{aid}/notes/{nid}":{"delete":{"operationId":"supprimer_note_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Note retirée de la mémoire"}},"tags":["general"]}},"/v1/projects/{pid}/agents/{aid}/politique-outils":{"get":{"operationId":"lire_politique_outils_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Politique d'outils granulaire de l'agent (absente : lecture seule par défaut pour les actions, tout outil de lecture passe)"},"404":{"description":"Agent inconnu"}},"tags":["general"]},"put":{"operationId":"poser_politique_outils_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolitiqueOutils"}}},"required":true},"responses":{"200":{"description":"Politique posée (chaque règle vise un outil DU CATALOGUE, arguments interdits DECLARÉS de cet outil)"},"400":{"description":"POLITIQUE_INVALIDE — chaque manquement nommé (outil inconnu, règle dupliquée, argument fantôme)"},"404":{"description":"Agent inconnu"}},"tags":["general"]}},"/v1/projects/{pid}/agents/{aid}/rag/brancher":{"post":{"operationId":"brancher_rag_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Outil rag_chercher branché sur l'agent (indexable ensuite par POST /agents/{aid}/rag/indexer)"},"404":{"description":"Agent inconnu"},"409":{"description":"DEJA_BRANCHE — l'outil rag_chercher est déjà là"}},"tags":["general"]}},"/v1/projects/{pid}/agents/{aid}/rag/chercher":{"post":{"operationId":"chercher_rag_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChercherRagReq"}}},"required":true},"responses":{"200":{"description":"k passages les plus proches (similarité cosinus MESURÉE, tri déterministe) — extraits sourcés"}},"tags":["agents"]}},"/v1/projects/{pid}/agents/{aid}/rag/indexer":{"post":{"operationId":"indexer_rag_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IndexerRagReq"}}},"required":true},"responses":{"200":{"description":"Document découpé en chunks LOCALISÉS (¶n, découpe k) et indexés dans la base de l'agent"}},"tags":["agents"]}},"/v1/projects/{pid}/agents/{aid}/rag/repondre":{"post":{"operationId":"repondre_rag_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChercherRagReq"}}},"required":true},"responses":{"200":{"description":"Réponse ASSEMBLÉE des extraits sourcés ([fichier — passage] …) — aucun passage au-dessus du seuil : sans_reponse vrai, dit tel quel"}},"tags":["agents"]}},"/v1/projects/{pid}/agents/{aid}/runs":{"get":{"operationId":"runs_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Historique des runs de l'agent (les plus récents d'abord)"}},"tags":["general"]},"post":{"operationId":"lancer_run_agent","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunAgentReq"}}},"required":true},"responses":{"201":{"description":"Run ReAct complet : étapes, observation par outil, réponse, coût"}},"tags":["general"]}},"/v1/projects/{pid}/amplify":{"get":{"operationId":"lire_amplify","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Stacks fullstack (plan ordonné, coût dérivé)"}},"tags":["apps"]}},"/v1/projects/{pid}/amplify/build/plan":{"post":{"operationId":"plan_build_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanBuildReq"}}},"required":true},"responses":{"200":{"description":"Étapes EXACTES du buildpack (install, build, run) — commandes RENDUES, jamais exécutées ici ; une phase sans rien à faire est OMISE honnêtement"},"400":{"description":"PLAN_REFUSE — langage inconnu ou point d'entrée introuvable (app.py/server.js/main.go requis, ou un Procfile pour rust), raisons nommées"}},"tags":["apps"]}},"/v1/projects/{pid}/amplify/flux-git":{"get":{"operationId":"lister_flux_git","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Flux git-push déclarés — aucun secret dans les descripteurs : le jeton vit dans le coffre, seul son NOM voyage"}},"tags":["apps"]},"put":{"operationId":"poser_flux_git","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FluxGit"}}},"required":true},"responses":{"200":{"description":"Flux validé : stack stk-…, branche en forme, dépôt https SANS identifiants incorporés (userinfo @, ssh://, git@ refusés), secret NOMMÉ (jamais sa valeur)"},"400":{"description":"FLUX_GIT_INVALIDE — chaque manquement nommé"}},"tags":["apps"]}},"/v1/projects/{pid}/amplify/flux-git/circuit":{"post":{"operationId":"circuit_flux_git","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CircuitFluxGitReq"}}},"required":true},"responses":{"200":{"description":"Circuit COMPLET rendu en étapes numérotées : réception du webhook (jeton vérifié DEPUIS le secret nommé), détection, étapes du buildpack, déploiement — JAMAIS exécuté ici"},"400":{"description":"CIRCUIT_REFUSE — flux invalide ou manifeste indétectable, raisons nommées"},"404":{"description":"Flux inconnu pour ce dépôt/branche"}},"tags":["apps"]}},"/v1/projects/{pid}/amplify/previews":{"get":{"operationId":"lister_previews_amplify","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Previews VIVANTES de l'org + la PURGE RENDUE : les URLs expirées (maintenant ≥ expire_le) sortent du registre et sont listées comme à détruire côté intégrateur — la destruction du service lui appartient, DIT"}},"tags":["apps"]},"post":{"operationId":"creer_preview_amplify","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Hôte public des previews (posé par l'intégrateur — l'URL en est dérivée)","in":"query","name":"hote","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreerPreviewReq"}}},"required":true},"responses":{"201":{"description":"Preview éphémère créée : branche VALIDÉE (slug [a-z0-9-] 1-40, jamais main/master — les branches stables ne sont pas des previews), URL dédiée https://{branche}-{org}.{hote}, expiration à NOW + 24 h (TTL DIT, posé ici)"},"400":{"description":"PREVIEW_REFUSEE — branche hors slug, bord en tiret, main/master, ou hôte absent"}},"tags":["apps"]}},"/v1/projects/{pid}/amplify/rollback":{"get":{"operationId":"rollback_amplify_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom réel de l'app déployée (pile explicite)","in":"query","name":"app","required":false,"schema":{"type":"string"}},{"description":"Nom DNS réel servi par la pile","in":"query","name":"dns","required":false,"schema":{"type":"string"}},{"description":"Nom réel de la base","in":"query","name":"base","required":false,"schema":{"type":"string"}},{"description":"Versions déployées de la plus ancienne à la plus récente, séparées par des virgules (N-1 = l'avant-dernière)","in":"query","name":"versions","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Le CONTRAT du rollback (3 étapes ORDONNÉES app → dns → base, l'inverse du déploiement, chacune vers N-1, RENDUES jamais exécutées) + le plan de la pile EXPLICITE si fournie ; les stacks réelles de l'org ne portent AUCUN historique de versions à ce jour — la pile réelle n'est pas représentée, c'est DIT"},"400":{"description":"ROLLBACK_REFUSE — pile incomplète (app, dns, base et versions exigés ensemble) ou moins de 2 versions (rien où revenir)"}},"tags":["apps"]}},"/v1/projects/{pid}/amplify/runtime/detecter":{"post":{"operationId":"detecter_runtime_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DetectionRuntimeReq"}}},"required":true},"responses":{"200":{"description":"Exactement UN runtime reconnu : langage, buildpack et fichiers du manifeste qui ONT déclenché la reconnaissance"},"400":{"description":"RUNTIME_INCONNU / RUNTIME_CONFLIT — aucun fichier caractéristique (rien n'est deviné) ou plusieurs runtimes concurrents, chaque fautif nommé"}},"tags":["apps"]}},"/v1/projects/{pid}/amplify/stacks":{"post":{"operationId":"creer_stack_amplify","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewStackReq"}}},"required":true},"responses":{"201":{"description":"Stack en brouillon avec son plan de déploiement"}},"tags":["apps"]}},"/v1/projects/{pid}/amplify/stacks/{sid}":{"delete":{"operationId":"supprimer_stack_amplify","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Stack retirée du registre"}},"tags":["apps"]}},"/v1/projects/{pid}/amplify/stacks/{sid}/deployer":{"post":{"operationId":"deployer_stack_amplify","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plan exécuté contre les vrais services (app+base+secret+dns+cdn)"}},"tags":["apps"]}},"/v1/projects/{pid}/amplify/stacks/{sid}/detruire":{"post":{"operationId":"detruire_stack_amplify","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"App et enregistrement DNS retirés, stack détruite"}},"tags":["apps"]}},"/v1/projects/{pid}/anomalies":{"get":{"operationId":"lister_anomalies","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["securite"]},"post":{"operationId":"scanner_anomalies","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"Joue les 4 heuristiques (H1-H4) sur toutes les clés des membres de l'org.\nEn production : scheduler 5 min ; ici : à la demande (mêmes calculs).","tags":["securite"]}},"/v1/projects/{pid}/apps":{"get":{"description":"Anomalie A7 (16/09) : workspaces et apps partagent le registre — la\nliste mélangeait les deux sans les distinguer (double comptage côté\nclients). Chaque ligne porte désormais un champ additif `genre`\n(« app » | « workspace », marqueur = tunnel_token) et des compteurs\n`apps`/`workspaces` ; les lignes restent toutes servies pour ne pas\ncasser les clients existants.","operationId":"lister_apps","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Apps + état (running/parked) — champ genre par ligne : « app » ou « workspace »"}},"summary":"Liste les apps serverless d'un projet (P5-09).","tags":["apps"]},"post":{"operationId":"creer_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewAppReq"}}},"required":true},"responses":{"201":{"description":"App créée (running)"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/auth":{"get":{"operationId":"liaisons_auth_apps_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Les liaisons d'auth que le produit SAIT brancher, calquées des capacités RÉELLES du module auth/amplify : fédération OIDC (émetteur Keycloak, JWKS RS256) et jetons d'app Ed25519 — rien de plus"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}":{"delete":{"operationId":"supprimer_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"App supprimée"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/auth":{"post":{"operationId":"plan_auth_app_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"App (app-…)","in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanAuthAppReq"}}},"required":true},"responses":{"200":{"description":"Plan d'auth de l'app RÉELLE : variables d'environnement PAR NOM SEULEMENT (la règle du produit interdit de rendre des valeurs — les secrets s'injectent via cirroSecrets au déploiement)"},"400":{"description":"PLAN_AUTH_REFUSE — liaison inconnue (oidc | jetons)"},"404":{"description":"App inconnue"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/canari":{"get":{"operationId":"lire_canari_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Canari courant : répartition et mesures MESURÉES des deux révisions"},"404":{"description":"App inconnue ou sans canari"}},"tags":["apps"]},"put":{"operationId":"poser_canari_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanariReq"}}},"required":true},"responses":{"200":{"description":"Canari posé : les révisions RÉELLES de l'app reçoivent la répartition (complément/poids — la somme fait toujours 100)"},"400":{"description":"CANARI_INVALIDE — poids hors bornes, révisions identiques"},"404":{"description":"App ou révision inconnue"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/canari/annuler":{"post":{"operationId":"annuler_canari_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rollback : 100 % du trafic revient au stable, le canari descend à 0 (listé pour l'analyse)"},"404":{"description":"App inconnue ou sans canari"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/canari/mesures":{"post":{"operationId":"poser_mesures_canari","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MesuresCanariReq"}}},"required":true},"responses":{"200":{"description":"Mesures enregistrées (remplacées) + verdict dérivé immédiat"},"404":{"description":"App inconnue ou sans canari"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/canari/promouvoir":{"post":{"operationId":"promouvoir_canari_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Promotion : la révision canari reçoit 100 % du trafic, le stable sort du trafic — le canari est clos"},"404":{"description":"App inconnue ou sans canari"},"409":{"description":"VERDICT_CONTRAIRE — le verdict mesuré n'est pas promouvoir (rien ne bouge)"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/canari/verdict":{"get":{"operationId":"verdict_canari_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Verdict MESURÉ : promouvoir (± tolérance) / annuler (régression) / continuer (pas assez de requêtes) — chaque branche cite ses nombres"},"404":{"description":"App inconnue ou sans canari"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/limites":{"get":{"operationId":"lire_limites_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Limites (instances RELUES à chaque GET — une app parkée a capacité 0)"},"404":{"description":"Aucune limite posée"}},"tags":["apps"]},"put":{"operationId":"poser_limites_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoserLimitesReq"}}},"required":true},"responses":{"200":{"description":"Limites posées — capacité = instances MESURÉES × concurrence"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/limites/admission":{"post":{"operationId":"admettre_requete_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdmissionReq"}}},"required":true},"responses":{"200":{"description":"Requête admise (+ requêtes en vol expirées au timeout, comptées)"},"429":{"description":"Refusée : capacité réelle et en-cours mesuré dans la réponse"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/metriques":{"get":{"operationId":"lire_metriques_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Compteurs MESURÉS + agrégats dérivés (aucun taux sans requête)"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/revisions":{"get":{"operationId":"lister_revisions","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Révisions et répartition du trafic"}},"tags":["apps"]},"post":{"operationId":"creer_revision","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NouvelleRevisionReq"}}},"required":true},"responses":{"201":{"description":"Révision IMMUABLE ajoutée — TRANCHE 7 (W7-3) : l'invariant du noyau s'applique (somme de trafic ∈ {0, 100}, image étiquetée exigée, quota respecté) ; sur une app déjà en service, posez la révision à 0 % puis basculez en PUT trafic"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/rollback":{"post":{"operationId":"rollback_app_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rollback N-1 : 100 % du trafic sur la révision IMMÉDIATEMENT antérieure à l'active, les autres hors trafic — l'id restauré est rendu"},"400":{"description":"ROLLBACK_REFUSE — moins de deux révisions, ou l'active est la première (aucun N-1)"},"404":{"description":"App inconnue"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/sante":{"delete":{"operationId":"retirer_sante_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Sonde retirée"}},"tags":["apps"]},"get":{"operationId":"lire_sante_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Santé sondée (compteurs MESURÉS, état DÉRIVÉ)"},"404":{"description":"Aucune sonde posée"}},"tags":["apps"]},"put":{"operationId":"poser_sante_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoserSanteReq"}}},"required":true},"responses":{"200":{"description":"Sonde posée (état initial « inconnu » — aucune mesure, aucun état déduit)"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/sante/sonder":{"post":{"operationId":"sonder_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Sonde exécutée MAINTENANT (code HTTP mesuré, état redérivé)"}},"summary":"La sonde RÉELLE : un GET HTTP sur l'URL de l'app + le chemin de\nsanté, timeout dur — le résultat (code, durée, erreur) est MESURÉ,\nl'état reste dérivé des compteurs consécutifs par le module.","tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/trafic":{"put":{"operationId":"basculer_trafic_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoidsTraficReq"}}},"required":true},"responses":{"200":{"description":"Bascule ATOMIQUE : chaque entrée validée AVANT toute mutation — au moindre refus aucun poids ne bouge ; les non-citées passent hors trafic, l'active devient la plus grosse part"},"400":{"description":"BASCULE_REFUSEE — entrée inconnue, doublon, part > 100 ou somme ≠ 100"},"404":{"description":"App inconnue"}},"tags":["apps"]}},"/v1/projects/{pid}/apps/{aid}/wake":{"post":{"operationId":"reveiller_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"App réveillée (running)"}},"summary":"Réveille une app endormie (scale-from-zero, P5-09).","tags":["apps"]}},"/v1/projects/{pid}/archives":{"get":{"operationId":"lister_archives","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Archives froides de l'org (tailles mesurées, ratios, rétention)"}},"tags":["compute"]},"post":{"operationId":"creer_archive","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewArchiveReq"}}},"required":true},"responses":{"201":{"description":"Archive créée — tar+zstd réellement écrit sur le stockage froid, empreinte sha256"}},"tags":["compute"]}},"/v1/projects/{pid}/archives/{aid}":{"delete":{"operationId":"supprimer_archive","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Archive supprimée du stockage froid et du registre"}},"tags":["compute"]},"get":{"operationId":"detail_archive","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'une archive froide — intégrité sha256 vérifiée sur le disque"}},"tags":["compute"]}},"/v1/projects/{pid}/archives/{aid}/restaurer":{"post":{"operationId":"restaurer_archive_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestaurerReq"}}},"required":true},"responses":{"200":{"description":"Archive décompressée et extraite — durée réellement mesurée"}},"tags":["compute"]}},"/v1/projects/{pid}/athena/executer":{"post":{"operationId":"executer_athena","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuterReq"}}},"required":true},"responses":{"200":{"description":"Résultat (colonnes + lignes), lecture seule"}},"tags":["general"]}},"/v1/projects/{pid}/athena/historique":{"get":{"operationId":"historique_athena","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"50 dernières exécutions (succès ET erreurs)"}},"tags":["general"]}},"/v1/projects/{pid}/athena/requetes":{"get":{"operationId":"lister_requetes_athena","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Requêtes nommées"}},"tags":["general"]},"post":{"operationId":"creer_requete_athena","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRequeteReq"}}},"required":true},"responses":{"201":{"description":"Requête nommée enregistrée"}},"tags":["general"]}},"/v1/projects/{pid}/athena/requetes/{rid}":{"delete":{"operationId":"supprimer_requete_athena","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Requête nommée supprimée"}},"tags":["general"]}},"/v1/projects/{pid}/athena/requetes/{rid}/executer":{"post":{"operationId":"executer_requete_athena","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Requête nommée exécutée"}},"tags":["general"]}},"/v1/projects/{pid}/athena/requetes/{rid}/export.csv":{"get":{"operationId":"exporter_requete_athena_csv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Id OU nom de la requête nommée","in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"CSV (RFC 4180) de la requête RÉ-EXÉCUTÉE sur les données réelles"}},"tags":["general"]}},"/v1/projects/{pid}/athena/requetes/{rid}/export.json":{"get":{"operationId":"exporter_requete_athena_json","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Id OU nom de la requête nommée","in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"JSON (un objet par ligne, cellules manquantes vides — jamais de null inventé) de la requête RÉ-EXÉCUTÉE"}},"tags":["general"]}},"/v1/projects/{pid}/athena/source/executer":{"post":{"operationId":"executer_source_athena","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceAthenaReq"}}},"required":true},"responses":{"200":{"description":"Requête exécutée sur la source fournie (lecture seule, typage déduit du contenu)"},"400":{"description":"SOURCE_INVALIDE / SQL_INVALIDE — contenu illisible, format inconnu ou requête refusée, raisons nommées"}},"tags":["general"]}},"/v1/projects/{pid}/athena/source/partitions":{"post":{"operationId":"executer_partitions_athena","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartitionsAthenaReq"}}},"required":true},"responses":{"200":{"description":"Exécution partitionnée : résultat global agrégé + résultats partiels MESURÉS par partition (count/sum s'additionnent, avg se moyenne pondérée)"},"400":{"description":"PARTITIONS_INVALIDES / SQL_INVALIDE — découpe ou requête refusée, raisons nommées"}},"tags":["general"]}},"/v1/projects/{pid}/athena/tables":{"get":{"operationId":"tables_athena","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tables réelles interrogeables (SQLite ou PostgreSQL)"}},"tags":["general"]}},"/v1/projects/{pid}/auth-apps":{"get":{"operationId":"lister_auth_apps","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Apps déclarées avec leur clé publique Ed25519 (vérifiable hors ligne) et le compte d'utilisateurs"}},"tags":["securite"]}},"/v1/projects/{pid}/auth-apps/{app}/jetons/emettre":{"post":{"operationId":"emettre_jeton_app_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"app","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmettreJetonAppReq"}}},"required":true},"responses":{"200":{"description":"Jeton signé Ed25519 (charge + signature hex) — vérifiable hors ligne par la clé publique de l'app"},"400":{"description":"JETON_REFUSE — utilisateur désactivé, TTL hors bornes"},"404":{"description":"App ou utilisateur inconnu"}},"tags":["securite"]}},"/v1/projects/{pid}/auth-apps/{app}/jetons/verifier":{"post":{"operationId":"verifier_jeton_app_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"app","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifierJetonAppReq"}}},"required":true},"responses":{"200":{"description":"Rôles rendus — signature Ed25519, fenêtre de validité et app attendue vérifiées"},"400":{"description":"JETON_INVALIDE — signature, expiration, app : chaque écart est nommé"},"404":{"description":"App inconnue"}},"tags":["securite"]}},"/v1/projects/{pid}/auth-apps/{app}/oidc/autoriser":{"post":{"operationId":"ouvrir_demande_oidc","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"app","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OidcAutoriserReq"}}},"required":true},"responses":{"200":{"description":"Demande d'autorisation OUVERTE (response_type=code forcé) — expire en 10 minutes"},"400":{"description":"DEMANDE_REFUSEE — issuer https, client_id en label, redirect https sans fragment"},"404":{"description":"App inconnue"}},"tags":["securite"]}},"/v1/projects/{pid}/auth-apps/{app}/oidc/callback":{"post":{"operationId":"traiter_callback_oidc","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"app","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OidcCallbackReq"}}},"required":true},"responses":{"200":{"description":"Code d'autorisation délivré — usage UNIQUE, échange dans la minute"},"400":{"description":"STATE_INVALIDE / DEMANDE_EXPIREE — chaque écart est nommé"},"404":{"description":"App inconnue ou demande non ouverte"}},"tags":["securite"]}},"/v1/projects/{pid}/auth-apps/{app}/oidc/discovery":{"get":{"operationId":"decouverte_oidc_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"app","required":true,"schema":{"type":"string"}},{"description":"Issuer https:// de l'app (ex. https://apps.acme.fr) — les endpoints en dérivent","in":"query","name":"issuer","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Document de discovery OIDC-lite rendu tel quel + limites DITES (RS256 externe non couvert, Ed25519 couvert)"},"400":{"description":"ISSUER_INVALIDE — https:// exigé, ni query ni fragment"}},"tags":["securite"]}},"/v1/projects/{pid}/auth-apps/{app}/oidc/token":{"post":{"operationId":"echanger_code_oidc","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"app","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OidcTokenReq"}}},"required":true},"responses":{"200":{"description":"id_token signé Ed25519 (EdDSA) — claims iss/sub/aud/nonce/iat/exp, chaque écart d'échange est nommé (CLIENT_INVALIDE, REDIRECT_DIFFERENT, CODE_EXPIRE, CODE_DEJA_UTILISE)"},"400":{"description":"ECHANGE_REFUSE — chaque écart est nommé"},"404":{"description":"App inconnue ou code non délivré"}},"tags":["securite"]}},"/v1/projects/{pid}/auth-apps/{app}/utilisateurs":{"get":{"operationId":"lister_utilisateurs_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"app","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Utilisateurs de l'app (rôles PAR APP — ils ne traversent pas les apps)"},"404":{"description":"App inconnue"}},"tags":["securite"]},"post":{"operationId":"inscrire_utilisateur_app_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"app","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InscrireUtilisateurAppReq"}}},"required":true},"responses":{"201":{"description":"Utilisateur inscrit — l'app naît au premier inscrit (paire de clés Ed25519 générée)"},"400":{"description":"INSCRIPTION_REFUSEE — e-mail malformé, rôle hors catalogue (admin, membre, lecteur), doublon"}},"tags":["securite"]}},"/v1/projects/{pid}/auth-apps/{app}/utilisateurs/{uid}":{"put":{"operationId":"maj_utilisateur_app","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"app","required":true,"schema":{"type":"string"}},{"in":"path","name":"uid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajUtilisateurAppReq"}}},"required":true},"responses":{"200":{"description":"Action appliquée — retirer le DERNIER rôle est refusé (désactivez l'utilisateur)"},"400":{"description":"ACTION_INVALIDE / ROLE_REFUSE — action inconnue ou garde du noyau franchie (refus nommé)"},"404":{"description":"App ou utilisateur inconnu"}},"tags":["securite"]}},"/v1/projects/{pid}/autoscale":{"get":{"operationId":"lister_autoscale","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Groupes d'auto-scaling de l'organisation"}},"tags":["calcul"]},"post":{"operationId":"creer_groupe_scaling","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewGroupeReq"}}},"required":true},"responses":{"201":{"description":"Groupe créé, bornes min appliquées immédiatement"}},"tags":["calcul"]}},"/v1/projects/{pid}/autoscale/{gid}":{"delete":{"operationId":"supprimer_groupe_scaling","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"gid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Groupe supprimé (les VMs créées sont conservées)"}},"tags":["calcul"]},"post":{"operationId":"ticker_groupe","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"gid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Un tick du groupe : décision appliquée (ou cooldown/rien)"}},"tags":["calcul"]},"put":{"operationId":"metriques_groupe","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"gid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"Métriques CPU réelles injectées dans le groupe (source : driver/opérateur)"}},"tags":["calcul"]}},"/v1/projects/{pid}/autoscale/{gid}/metriques":{"get":{"operationId":"lister_metriques_perso","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"gid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Métriques personnalisées du groupe + dernière moyenne MESURÉE de chacune (fenêtre remplie ou rien)"},"404":{"description":"Groupe inconnu"}},"tags":["calcul"]},"post":{"operationId":"creer_metrique_perso","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"gid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MetriquePerso"}}},"required":true},"responses":{"201":{"description":"Métrique déclarée (seuils, fenêtre de décision)"},"400":{"description":"METRIQUE_INVALIDE — chaque manquement nommé"},"404":{"description":"Groupe inconnu"},"409":{"description":"DEJA_PRIS — une métrique porte déjà ce nom"}},"tags":["calcul"]}},"/v1/projects/{pid}/autoscale/{gid}/metriques/{nom}":{"delete":{"operationId":"supprimer_metrique_perso","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"gid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Métrique retirée (ses échantillons partent avec)"},"404":{"description":"Groupe ou métrique inconnue"}},"tags":["calcul"]}},"/v1/projects/{pid}/autoscale/{gid}/metriques/{nom}/echantillons":{"post":{"operationId":"poster_echantillons_metrique","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"gid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EchantillonsReq"}}},"required":true},"responses":{"200":{"description":"Échantillons ajoutés + moyenne de la fenêtre DÉRIVÉE (None si la fenêtre n'est pas remplie — honnête)"},"404":{"description":"Groupe ou métrique inconnue"}},"tags":["calcul"]}},"/v1/projects/{pid}/autoscale/{gid}/metriques/{nom}/prediction":{"get":{"operationId":"predire_scaling_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"gid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}},{"description":"Taille de la moyenne mobile (échantillons)","in":"query","name":"fenetre","required":true,"schema":{"format":"int32","minimum":0,"type":"integer"}},{"description":"Fenêtres projetées (1-12)","in":"query","name":"horizon","required":true,"schema":{"format":"int32","minimum":0,"type":"integer"}},{"description":"Borne basse d'écrêtage","in":"query","name":"borne_basse","required":true,"schema":{"format":"double","type":"number"}},{"description":"Borne haute d'écrêtage","in":"query","name":"borne_haute","required":true,"schema":{"format":"double","type":"number"}}],"responses":{"200":{"description":"Prédiction sur l'historique RÉEL des échantillons posés (W6-7) : moyenne mobile mesurée, tendance mesurée si deux fenêtres, projection ÉCRÊTÉE aux bornes, intervalle honnête ± écart-type mesuré"},"400":{"description":"PREDICTION_REFUSEE — fenêtre/horizon hors bornes, bornes inversées, historique trop court ou non fini"},"404":{"description":"Groupe ou métrique inconnue, sans échantillon"}},"tags":["calcul"]}},"/v1/projects/{pid}/autoscale/{gid}/plages":{"get":{"operationId":"lister_plages_groupe","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"gid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plages posées + bornes EFFECTIVES à l'heure UTC courante + décision du tick sous politique"}},"tags":["calcul"]},"put":{"operationId":"poser_plages_groupe","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"gid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PlagePlanifiee"},"type":"array"}}},"required":true},"responses":{"200":{"description":"Plages remplacées (heures UTC ; une plage nocturne 22→6 traverse minuit ; les bornes restent écrêtées par le max du groupe)"},"400":{"description":"Plage invalide"}},"tags":["calcul"]}},"/v1/projects/{pid}/autoscale/{gid}/sante":{"post":{"operationId":"verdict_sante_groupe","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"gid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SantesReq"}}},"required":true},"responses":{"200":{"description":"Décision d'autohealing DÉRIVÉE des états mesurés : malades actifs à retirer, remplacements bornés par min/max — rien n'est exécuté ici"},"404":{"description":"Groupe inconnu"}},"tags":["calcul"]}},"/v1/projects/{pid}/backups":{"get":{"operationId":"lister_plans_backup","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plans de sauvegarde planifiée de l'org (du/prochaine échéance calculés)"}},"tags":["compute"]},"post":{"operationId":"creer_plan_backup","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewPlanReq"}}},"required":true},"responses":{"201":{"description":"Plan de sauvegarde créé — le scheduler déclenchera les vzdump"}},"tags":["compute"]}},"/v1/projects/{pid}/backups/catalogue":{"get":{"operationId":"chercher_catalogue_backups","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom de fichier à chercher (dernier segment du chemin, insensible à la casse) — vide : refus honnête, pas de réponse « tout »","in":"query","name":"q","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Occurrences datées du fichier dans les manifestes de l'org (une par sauvegarde qui le couvre, la plus ancienne d'abord) + total de l'index"},"400":{"description":"RECHERCHE_INVALIDE — requête vide"}},"tags":["stockage"]},"post":{"operationId":"deposer_manifeste_backup","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManifesteSauvegardeReq"}}},"required":true},"responses":{"201":{"description":"Manifeste déposé (remplace celui du même backup_id s'il existait) : l'inventaire fichiers vient de l'appelant, le plan de contrôle ne le devine pas"},"400":{"description":"MANIFESTE_INVALIDE — backup_id/machine vides, date illisible, aucun fichier"}},"tags":["stockage"]}},"/v1/projects/{pid}/backups/couverture":{"get":{"operationId":"lire_couverture_backup","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Bundle archivé + inventaire réel par famille (le bundle cite des noms lus des registres, pas des demandes)"}},"tags":["stockage"]},"put":{"operationId":"poser_couverture_backup","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoserCouvertureReq"}}},"required":true},"responses":{"200":{"description":"Bundle construit (les manquants sont TOUS cités — rien n'est construit à moitié)"},"400":{"description":"Genre inconnu ou id introuvable dans l'inventaire réel"}},"tags":["stockage"]}},"/v1/projects/{pid}/backups/tests-restauration":{"get":{"operationId":"lister_tests_restauration","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plans de test de restauration de l'org + les VERDICTS : chaque résultat journalisé par l'exécutant (Go relus MESURÉS, durée, taille de la source) est jugé OK (≥ 99,5 % de la source restaurée — mesuré, pas déclaré) / PARTIEL (l'écart en % est nommé) / ÉCHEC (rien de restauré ou chiffres non exploitables) ; sans résultat journalisé, le plan est rendu avec VIDE, dit tel quel"}},"tags":["compute"]},"post":{"operationId":"creer_test_restauration","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanTestRestaurationReq"}}},"required":true},"responses":{"201":{"description":"Plan de test de restauration posé, VALIDÉ contre les plans RÉELS de l'org (un test ne vise jamais un plan fantôme) : machine témoin jetable, fenêtre cron forme simple, prochaine occurrence calculée ; l'EXÉCUTION (restaurer sur la machine témoin et mesurer les Go relus) appartient à l'intégration — le verdict se déduit des chiffres, il ne se déclare pas"},"400":{"description":"PLAN_TEST_REFUSE — refus NOMMÉ : plan de sauvegarde introuvable parmi les plans réels, machine témoin vide, fenêtre cron non supportée (champ coupable cité)"},"409":{"description":"DEJA_PRIS — un plan de test existe déjà pour ce plan de sauvegarde"}},"tags":["compute"]}},"/v1/projects/{pid}/backups/{bid}":{"delete":{"operationId":"supprimer_plan_backup","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plan supprimé (les backups déjà pris restent sur le stockage)"}},"tags":["compute"]},"get":{"operationId":"detail_plan_backup","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plan de sauvegarde (due/prochaine échéance calculés)"}},"tags":["compute"]},"put":{"operationId":"maj_plan_backup","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajPlanReq"}}},"required":true},"responses":{"200":{"description":"Plan modifié (fréquence, fenêtre, rétention, pause)"}},"tags":["compute"]}},"/v1/projects/{pid}/backups/{bid}/executer":{"post":{"operationId":"executer_plan_backup_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Backup vzdump lancé maintenant + rétention appliquée (503 honnête sans Proxmox)"}},"tags":["compute"]}},"/v1/projects/{pid}/bascule/exercices":{"get":{"operationId":"lire_plan_exercices","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plan d'exercices blancs (première occurrence, intervalle) + prochaines occurrences DÉRIVÉES sur 30 jours"}},"tags":["general"]},"put":{"operationId":"poser_plan_exercices","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanExercice"}}},"required":true},"responses":{"200":{"description":"Plan posé — les occurrences se dérivent, elles ne se stockent pas"},"400":{"description":"PLAN_INVALIDE — org ou intervalle"}},"tags":["general"]}},"/v1/projects/{pid}/bascule/exercices/archives":{"get":{"operationId":"archives_exercices_blancs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rapports d'exercices archivés (verdict DÉRIVÉ des phases, jamais saisi)"}},"tags":["general"]}},"/v1/projects/{pid}/bascule/exercices/executer":{"post":{"operationId":"executer_exercice_blanc_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuterExerciceReq"}}},"required":true},"responses":{"200":{"description":"Exercice BLANC exécuté en simulation pure : sondes, décision (site coupé dans la simulation), resynchronisation sur COPIES des records — verdict DÉRIVÉ des phases, rapport archivé"},"404":{"description":"Zone inconnue"}},"tags":["general"]}},"/v1/projects/{pid}/bascule/moniteurs":{"get":{"operationId":"lister_moniteurs_bascule","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Moniteurs posés + décision de chacun (Rien | Compter | Basculer) selon la santé MESURÉE du repli"}},"tags":["gouvernance"]},"post":{"operationId":"creer_moniteur_bascule","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoniteurService"}}},"required":true},"responses":{"201":{"description":"Moniteur créé (criticité 1-5, seuil d'échecs ≥ 1)"},"400":{"description":"Moniteur invalide"},"409":{"description":"Service déjà surveillé"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/bascule/moniteurs/{service}":{"delete":{"operationId":"supprimer_moniteur_bascule","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"service","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Moniteur retiré"},"404":{"description":"Moniteur inconnu"}},"tags":["gouvernance"]},"put":{"operationId":"maj_moniteur_bascule","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"service","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoniteurService"}}},"required":true},"responses":{"200":{"description":"Moniteur remplacé (typiquement : échecs consécutifs MESURÉS mis à jour par le surveillant)"},"404":{"description":"Moniteur inconnu"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/bascule/priorites":{"get":{"operationId":"priorites_bascule","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Santé MESURÉE du site de repli (défaut : sondée en direct)","in":"query","name":"repli_sain","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Demandes de bascule priorisées (criticité 1 = vital d'abord) — vides si personne ne réclame"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/bascule/runbook":{"get":{"operationId":"runbook_bascule","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Transition motivante : bascule_auto | retour_manuel","in":"query","name":"transition","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Runbook de RETOUR généré (étapes numérotées + commandes exactes + FQDN/IP attendus — écrit AVANT d'en avoir besoin)"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/bases/pgwire":{"get":{"operationId":"harnais_pgwire","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Harnais de compatibilité pgwire : les 12 cas (9 familles nommées) avec leur SQL, leur attendu ET la note_pg18 (le comportement d'un VRAI PostgreSQL 18) ; l'ÉVALUATION contre un vrai PostgreSQL appartient à l'intégration — elle joue chaque SQL et rend UNE observation, le harnais la compare et publie rapport_diffs — DIT, aucun verdict simulé ici"}},"tags":["donnees"]}},"/v1/projects/{pid}/bases/postgres/offre":{"get":{"operationId":"offre_postgres","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom de l'instance (slug) — rend le docker run EXACT + le plan de sauvegarde pg_dump -Fc 14 jours, JAMAIS exécutés ici (cirrod lance et mesure)","in":"query","name":"hote","required":false,"schema":{"type":"string"}},{"description":"Plan de l'instance : postgres-nano | postgres-small | postgres-medium (défaut postgres-small)","in":"query","name":"plan","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Offre PostgreSQL managé — DÉCISION UTILISATEUR 1.a DU 02/10 : le SQL relationnel managé du lab est du VRAI PostgreSQL (18.6 épinglé, licence PostgreSQL dite) sur le pattern exact de cirroCache/Valkey : consts épinglées, plans dérivés, commande docker EXACTE rendue jamais exécutée, mot de passe par FICHIER référencé (POSTGRES_PASSWORD_FILE=/run/secrets/pg_pass, jamais en clair), garde de plan qui refuse ce qui trahirait l'offre"},"400":{"description":"PLAN_INCONNU / PLAN_INSANE — plan hors catalogue, ou plan ne passant pas sa propre garde (image non épinglée, mot de passe en clair, volume absent)"}},"tags":["donnees"]}},"/v1/projects/{pid}/bouclier":{"get":{"operationId":"lire_bouclier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Posture défensive vérifiée contre l'état réel + réponse recommandée"}},"tags":["securite"]}},"/v1/projects/{pid}/bouclier/abonnement":{"post":{"operationId":"abonner_bouclier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewAbonnementReq"}}},"required":true},"responses":{"200":{"description":"Niveau d'abonnement changé (0 € assumé sur LAN)"}},"tags":["securite"]}},"/v1/projects/{pid}/bouclier/journal":{"get":{"operationId":"journal_bouclier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Attaques agrégées des bans et refus RÉELS du pare-feu"}},"tags":["securite"]}},"/v1/projects/{pid}/buckets":{"get":{"operationId":"lister_buckets","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BucketsListe"}}},"description":"Buckets du registre — compteurs en direct (source s3) quand la passerelle répond, repli honnête (source registre) sinon ; les buckets créés hors produit sont SIGNALÉS, jamais adoptés"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Jeton absent/invalide"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"HORS_ORGANISATION / PERMISSION_REFUSEE — project.read requis"}},"tags":["donnees"]},"post":{"operationId":"creer_bucket","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewBucketReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BucketCree"}}},"description":"Bucket créé sur la passerelle S3 réelle ET au registre (backend d'abord : un refus ne laisse rien au registre)"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Jeton absent/invalide"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"HORS_ORGANISATION / PERMISSION_REFUSEE — project.create requis"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"DEJA_EXISTANT — le nom est déjà pris dans l'org"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"SERVICE_INDISPONIBLE — stockage objet injoignable"}},"tags":["donnees"]}},"/v1/projects/{pid}/buckets/{bid}":{"delete":{"operationId":"supprimer_bucket","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["donnees"]},"get":{"operationId":"detail_bucket","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["donnees"]}},"/v1/projects/{pid}/budgets":{"get":{"operationId":"lister_budgets","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Budgets + évaluations (showback vs limites)"}},"tags":["donnees"]},"post":{"operationId":"creer_budget","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewBudgetReq"}}},"required":true},"responses":{"201":{"description":"Budget créé (seuils 50/80/100 %)"}},"tags":["donnees"]}},"/v1/projects/{pid}/budgets/periodes":{"get":{"operationId":"lister_periodes_budgets","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Évaluations des budgets sur leurs plages (consommation mesurée par factures + mois courant vivant)"}},"tags":["donnees"]}},"/v1/projects/{pid}/budgets/periodes/{perid}":{"delete":{"operationId":"supprimer_periode_budget","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"perid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plage retirée du budget"}},"tags":["donnees"]}},"/v1/projects/{pid}/budgets/portees":{"get":{"operationId":"lister_budgets_portees","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Budgets à périmètre : évaluations (showback dedans/dehors) + décisions d'actions"}},"tags":["donnees"]},"post":{"operationId":"creer_budget_portee","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BudgetPorteeReq"}}},"required":true},"responses":{"201":{"description":"Budget à périmètre créé"},"400":{"description":"Périmètre invalide ou limite nulle"}},"tags":["donnees"]}},"/v1/projects/{pid}/budgets/portees/{bid}":{"delete":{"operationId":"supprimer_budget_portee","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Budget à périmètre supprimé (politique d'actions avec)"}},"tags":["donnees"]}},"/v1/projects/{pid}/budgets/portees/{bid}/actions":{"get":{"operationId":"lire_actions_budget_portee","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Politique d'actions du budget (défaut 80/100 si jamais posée)"}},"tags":["donnees"]},"put":{"operationId":"poser_actions_budget_portee","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolitiqueActionsReq"}}},"required":true},"responses":{"200":{"description":"Politique posée — le garde des créations l'applique dès la requête suivante"},"400":{"description":"Seuils incohérents"}},"tags":["donnees"]}},"/v1/projects/{pid}/budgets/portees/{bid}/evaluation":{"get":{"operationId":"evaluer_budget_portee","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Évaluation du périmètre (showback dedans/dehors) + décision d'actions"}},"tags":["donnees"]}},"/v1/projects/{pid}/budgets/rapport-execution":{"get":{"operationId":"rapport_execution_budgets","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"début inclus AAAA-MM-JJ (défaut : 1er du mois courant)","in":"query","name":"debut","required":false,"schema":{"type":"string"}},{"description":"fin incluse AAAA-MM-JJ (défaut : aujourd'hui)","in":"query","name":"fin","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Comptage des actions préventives de la période + économie cumulée mesurée"}},"tags":["donnees"]}},"/v1/projects/{pid}/budgets/{bid}":{"delete":{"operationId":"supprimer_budget","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Budget supprimé"}},"tags":["donnees"]}},"/v1/projects/{pid}/budgets/{bid}/periodes":{"post":{"operationId":"creer_periode_budget","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewBudgetPeriodeReq"}}},"required":true},"responses":{"201":{"description":"Budget étendu à une plage de dates (trimestre, campagne…)"},"404":{"description":"Budget inconnu"}},"tags":["donnees"]}},"/v1/projects/{pid}/cache":{"get":{"operationId":"lister_caches","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Caches managés de l'org"}},"tags":["donnees"]},"post":{"operationId":"creer_cache","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewCacheReq"}}},"required":true},"responses":{"201":{"description":"Cache managé créé (veille) — l'offre tient en UNE commande, rendue telle quelle"}},"tags":["donnees"]}},"/v1/projects/{pid}/cache/offre":{"get":{"operationId":"offre_cache","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Offre cirroCache honnête : moteur Valkey épinglé (BSD-3, compatible Redis OSS 7.2), plans chiffrés, modes d'éviction, bornes TTL, ACL, docker run modèle rendu jamais exécuté"}},"tags":["donnees"]}},"/v1/projects/{pid}/cache/{cid}":{"get":{"operationId":"detail_cache","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Supervision du cache : état, résumé SI des métriques INFO existent, réplique chaude le cas échéant"}},"tags":["donnees"]}},"/v1/projects/{pid}/cache/{cid}/bascule":{"post":{"operationId":"basculer_cache","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BasculeCacheReq"}}},"required":true},"responses":{"200":{"description":"Décision de bascule (perte possible CHIFFRÉE si la réplique était en retard) puis exécution le cas échéant"}},"tags":["donnees"]}},"/v1/projects/{pid}/cache/{cid}/supervision":{"post":{"operationId":"superviser_cache","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupervisionCacheReq"}}},"required":true},"responses":{"200":{"description":"Décision de supervision APPLIQUÉE (redémarrage compté sur état mesuré) + résumé si métriques fournies"}},"tags":["donnees"]}},"/v1/projects/{pid}/carte":{"get":{"operationId":"voir_carte","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"filtre « cle » ou « cle=valeur » sur les labels posés","in":"query","name":"label","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"cirroCarte : noms de services découverts, conflits, dérive (filtrables par label)"}},"tags":["reseau"]}},"/v1/projects/{pid}/carte/dns":{"get":{"operationId":"resoudre_dns_carte","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom à résoudre dans la zone <org>.cirro","in":"query","name":"nom","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Réponse A/AAAA/CNAME des entrées PUBLIÉES — inconnu rend « inconnu », jamais une valeur devinée"}},"tags":["reseau"]}},"/v1/projects/{pid}/carte/labels":{"get":{"operationId":"lire_labels_carte","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Labels posés par service de la carte"}},"tags":["reseau"]},"post":{"operationId":"poser_labels_carte","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoserLabelsCarteReq"}}},"required":true},"responses":{"201":{"description":"Labels posés sur le service (remplacement)"},"404":{"description":"Nom de service inconnu de la carte composée"}},"tags":["reseau"]}},"/v1/projects/{pid}/carte/labels/retirer":{"post":{"operationId":"retirer_labels_carte","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetirerLabelsCarteReq"}}},"required":true},"responses":{"200":{"description":"Labels retirés du service"}},"tags":["reseau"]}},"/v1/projects/{pid}/carte/regenerer":{"post":{"operationId":"regenerer_carte","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Zone assurée + enregistrements auto publiés/retirés, conflits listés"}},"tags":["reseau"]}},"/v1/projects/{pid}/carte/sante":{"get":{"operationId":"lire_sante_carte","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"États mesurés + verdicts DÉRIVÉS par service (jamais sondé = inconnu, dit tel quel)"}},"tags":["reseau"]}},"/v1/projects/{pid}/carte/sante/propositions-retrait":{"get":{"operationId":"propositions_retrait_carte","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Propositions de retrait (seuil d'échecs MESURÉS atteint) — PROPOSÉES, jamais exécutées : l'opérateur confirme"}},"tags":["reseau"]}},"/v1/projects/{pid}/carte/sante/verdict":{"post":{"operationId":"pousser_verdict_sante_carte","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerdictSanteReq"}}},"required":true},"responses":{"200":{"description":"Verdict intégré (compteurs recomptés, état redérivé)"},"404":{"description":"Service inconnu de la zone"}},"tags":["reseau"]}},"/v1/projects/{pid}/cdn":{"get":{"operationId":"lister_distributions_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Distributions CDN — stats de cache mesurées (hits/misses/octets économisés)"}},"tags":["reseau"]},"post":{"operationId":"creer_distribution_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewDistributionReq"}}},"required":true},"responses":{"201":{"description":"Distribution créée — proxy cache devant l'origine"}},"tags":["reseau"]}},"/v1/projects/{pid}/cdn/{did}":{"delete":{"operationId":"supprimer_distribution_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Distribution supprimée — cache vidé"}},"tags":["reseau"]},"get":{"operationId":"detail_distribution_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'une distribution — stats mesurées"}},"tags":["reseau"]}},"/v1/projects/{pid}/cdn/{did}/invalider":{"post":{"operationId":"invalider_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"Cache invalidé — tout, un chemin précis (clé exacte) ou un préfixe de chemin"}},"tags":["reseau"]}},"/v1/projects/{pid}/cdn/{did}/origines":{"get":{"operationId":"lire_origines_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Origines multiples + décision de failover DÉRIVÉE des santés mesurées (aucune réponse inventée si tout est au sol)"}},"tags":["reseau"]},"put":{"operationId":"poser_origines_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/OrigineReq"},"type":"array"}}},"required":true},"responses":{"200":{"description":"Origines remplacées (priorité 1 = préférée ; santé = mesurée par les sondes de l'appelant)"},"400":{"description":"URL invalide, priorité 0, doublon"}},"tags":["reseau"]}},"/v1/projects/{pid}/cdn/{did}/regles-cache":{"delete":{"operationId":"retirer_regle_cache_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}},{"description":"Id de la règle à retirer","in":"query","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règle retirée"},"404":{"description":"Règle inconnue"}},"tags":["reseau"]},"get":{"operationId":"lister_regles_cache_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles posées (première qui correspond gagne, sinon défaut de la distribution)"}},"tags":["reseau"]},"post":{"operationId":"ajouter_regle_cache_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegleCacheReq"}}},"required":true},"responses":{"201":{"description":"Règle AJOUTÉE en fin de liste (l'ordre de déclaration = ordre d'évaluation)"},"409":{"description":"Id de règle déjà pris"}},"tags":["reseau"]}},"/v1/projects/{pid}/cdn/{did}/regles-cache/evaluer":{"post":{"operationId":"evaluer_regles_cache_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContexteRequeteReq"}}},"required":true},"responses":{"200":{"description":"Décision PURE pour ce contexte observé (règle responsable citée, sinon défaut)"}},"tags":["reseau"]}},"/v1/projects/{pid}/cdn/{did}/servir":{"get":{"operationId":"servir_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}},{"in":"query","name":"chemin","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Contenu servi — X-Cirro-Cache: hit|miss, latence origine mesurée"},"502":{"description":"Origine injoignable — jamais de contenu inventé"}},"tags":["reseau"]}},"/v1/projects/{pid}/cdn/{did}/temps-reel":{"get":{"operationId":"lire_temps_reel_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}},{"description":"Fenêtre glissante en secondes (défaut 60)","in":"query","name":"fenetre_s","required":false,"schema":{"format":"int64","minimum":0,"type":"integer"}}],"responses":{"200":{"description":"Fenêtre glissante MESURÉE (hits, octets, p50/p95 nearest-rank — vide = zéros honnêtes)"}},"tags":["reseau"]},"post":{"operationId":"pousser_echantillon_cdn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EchantillonCdnReq"}}},"required":true},"responses":{"200":{"description":"Échantillon MESURÉ enregistré (servi par l'edge, fourni par l'appelant) + fenêtre re-agrégée"}},"tags":["reseau"]}},"/v1/projects/{pid}/certificats":{"get":{"operationId":"lister_certificats","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Certificats émis pour l'organisation"}},"tags":["securite"]},"post":{"operationId":"creer_certificat","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewCertReq"}}},"required":true},"responses":{"201":{"description":"Certificat émis (90 j, SAN DNS) — clé privée rendue UNE fois"}},"tags":["securite"]}},"/v1/projects/{pid}/certificats/renouvellements":{"get":{"operationId":"lister_renouvellements_ca","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Chaque certificat : jours restants, échéance due au seuil J-30, état du suivi ACME"}},"tags":["securite"]}},"/v1/projects/{pid}/certificats/{cid}":{"delete":{"operationId":"supprimer_certificat","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Certificat révoqué (retiré du registre)"}},"tags":["securite"]}},"/v1/projects/{pid}/certificats/{cid}/renouvellement":{"post":{"operationId":"demarrer_renouvellement_cert","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Demande partie (défis DNS-01 à poser) — ou rien à faire si le certificat n'est pas dû"},"404":{"description":"Certificat inconnu"}},"tags":["securite"]}},"/v1/projects/{pid}/certificats/{cid}/renouvellement/defi-pose":{"post":{"operationId":"defi_pose_renouvellement","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Transition Demande → DefiPose enregistrée"},"404":{"description":"Certificat inconnu"},"409":{"description":"Aucune demande en cours, ou transition invalide"}},"tags":["securite"]}},"/v1/projects/{pid}/certificats/{cid}/renouvellement/echec":{"post":{"operationId":"echec_renouvellement_cert","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"Échec compté — reprise au prochain cycle, ou BLOQUÉ à 5 échecs (reprise manuelle)"}},"tags":["securite"]}},"/v1/projects/{pid}/certificats/{cid}/renouvellement/emettre":{"post":{"operationId":"emettre_renouvellement_cert","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Nouveau certificat émis (défis posés) — secrets déposés au coffre, suivi remis à zéro"},"404":{"description":"Certificat inconnu"},"409":{"description":"Les défis ne sont pas posés (état ≠ DefiPose)"}},"tags":["securite"]}},"/v1/projects/{pid}/chaos/noeud/{zone}":{"post":{"operationId":"chaos_tuer_noeud","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"zone","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"P4-15 — Tuer un nœud : les VMs critiques (protection or/argent) basculent\nvers les zones saines, les autres attendent, post-mortem dans l'audit.","tags":["automatisation"]}},"/v1/projects/{pid}/chaos/plan":{"get":{"operationId":"lire_plan_chaos","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plan courant + prochaine occurrence + échéance DUE"},"404":{"description":"Aucun plan posé"}},"tags":["securite"]},"put":{"operationId":"poser_plan_chaos","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanChaosReq"}}},"required":true},"responses":{"200":{"description":"Plan de chaos posé (validé par le module) + prochaine occurrence STRICTEMENT future"}},"tags":["securite"]}},"/v1/projects/{pid}/chaos/planifier":{"post":{"operationId":"planifier_chaos_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Exécution planifiée (ids DÉTERMINISTES dérivés de l'occurrence) — RENDUE, l'expérience elle-même n'est pas lancée ici"}},"tags":["securite"]}},"/v1/projects/{pid}/chaos/post-mortems":{"get":{"operationId":"lister_post_mortems_chaos","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Post-mortems archivés (les plus récents d'abord)"}},"tags":["securite"]},"post":{"operationId":"generer_post_mortem_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostMortemChaosReq"}}},"required":true},"responses":{"201":{"description":"Post-mortem généré : chronologie triée, délais MESURÉS depuis le début, bilan compté"}},"tags":["securite"]}},"/v1/projects/{pid}/comprehend":{"get":{"operationId":"lister_analyses_comprehend","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Historique des analyses — le texte analysé n'est jamais stocké"}},"tags":["general"]},"post":{"operationId":"analyser_texte_comprehend","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyserTexteReq"}}},"required":true},"responses":{"201":{"description":"Analyse réelle locale — sentiment, entités, langue, phrases clés"}},"tags":["general"]}},"/v1/projects/{pid}/comprehend/classer":{"post":{"operationId":"classer_texte_comprehend","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClasserTexteReq"}}},"required":true},"responses":{"200":{"description":"Étiquette élue (ou incertain), similarité MESURÉE et votes publics — le texte n'est jamais stocké"},"400":{"description":"Texte vide, exemples insuffisants, k/seuil hors bornes"}},"tags":["general"]}},"/v1/projects/{pid}/comprehend/lot":{"post":{"operationId":"analyser_lot_comprehend","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyserLotReq"}}},"required":true},"responses":{"201":{"description":"Lot analysé : analyses paginées dans l'ordre des textes + rejets explicites"},"409":{"description":"Historique plein — la page ne rentre pas"}},"summary":"Traitement par lot cirroComprehend (FLUX W-7) : chaque texte suit\nEXACTEMENT le chemin de l'analyse unitaire (mêmes bornes, mêmes\nrègles) — le lot évite N appels HTTP, il n'invente rien. Les textes\nrefusés partent dans `rejets` avec leur index d'origine, jamais en\nsilence. Les analyses de la page demandée entrent à l'historique\ndans la limite de capacité (refus franc au-delà, comme l'unitaire).","tags":["general"]}},"/v1/projects/{pid}/comprehend/ner":{"post":{"operationId":"ner_comprehend","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NerReq"}}},"required":true},"responses":{"200":{"description":"Entités nommées personne/lieu/organisation par GAZETIERS FERMÉS 100 % locaux (bornes de MOT exactes, texte source et bornes rendus) + la RICHESSE (genres distincts couverts, 0-3) ; les limites du gazetier fermé sont DITES : « Marie » hors liste n'est pas détecté, « Annemasse » n'est pas « Anne »"},"400":{"description":"NER_REFUSE — texte vide ou > 512 Kio caractères"}},"tags":["donnees"]}},"/v1/projects/{pid}/comprehend/resumer":{"post":{"operationId":"resumer_texte_comprehend","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResumerTexteReq"}}},"required":true},"responses":{"200":{"description":"Phrases les plus représentatives, dans l'ordre du texte — le texte n'est jamais stocké"},"400":{"description":"Texte vide ou nombre de phrases hors bornes"}},"tags":["general"]}},"/v1/projects/{pid}/comprehend/{aid}":{"delete":{"operationId":"supprimer_analyse_comprehend","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Analyse supprimée de l'historique"}},"tags":["general"]},"get":{"operationId":"detail_analyse_comprehend","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'une analyse — entités extraites (le texte source n'est pas stocké)"}},"tags":["general"]}},"/v1/projects/{pid}/config/evaluations":{"get":{"operationId":"lister_evaluations_config","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Dernière évaluation persistée (vide si jamais évaluée)"}},"tags":["securite"]}},"/v1/projects/{pid}/config/evaluer":{"post":{"operationId":"evaluer_config","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Évalue toutes les règles actives contre l'inventaire réel + score global"}},"tags":["securite"]}},"/v1/projects/{pid}/config/exceptions":{"get":{"operationId":"lister_exceptions_config","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Exceptions planifiées (actives et expirées)"}},"tags":["securite"]},"post":{"operationId":"creer_exception_config","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExceptionConfigReq"}}},"required":true},"responses":{"201":{"description":"Exception créée — appliquée à chaque évaluation tant qu'elle est active"},"400":{"description":"Motif trop court, dates illisibles ou inversées"}},"tags":["securite"]}},"/v1/projects/{pid}/config/exceptions/{xid}":{"delete":{"operationId":"supprimer_exception_config","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"xid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Exception retirée — l'écart revient de lui-même à la prochaine évaluation"},"404":{"description":"Exception inconnue"}},"tags":["securite"]}},"/v1/projects/{pid}/config/regles":{"get":{"operationId":"lister_regles_config","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles de conformité de l'org"}},"tags":["securite"]},"post":{"operationId":"creer_regle_config","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRegleConfigReq"}}},"required":true},"responses":{"201":{"description":"Règle créée et évaluée immédiatement contre l'inventaire réel"}},"tags":["securite"]}},"/v1/projects/{pid}/config/regles/expression":{"post":{"operationId":"evaluer_expression_config","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpressionReq"}}},"required":true},"responses":{"200":{"description":"Parse PUIS évalue l'expression contre le contexte de la ressource donnée : DSL borné DNF fail-closed (ET prioritaire sur OU, sans parenthèses), attributs d'une liste FERMÉE + tags.<cle>, > et < réservés à cout_mensuel ; un attribut ABSENT ne satisfait AUCUNE comparaison — pas même !="},"400":{"description":"EXPRESSION_INVALIDE — refus NOMMÉ : attribut/opérateur inconnu (cité), atome mal formé, incohérence de type, expression trop longue (> 500 caractères), trop profonde (> 12 combinaisons), inachevée"}},"tags":["securite"]}},"/v1/projects/{pid}/config/regles/{rid}":{"delete":{"operationId":"supprimer_regle_config","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règle supprimée"}},"tags":["securite"]}},"/v1/projects/{pid}/config/remediation":{"get":{"operationId":"lister_remediation_config","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Politiques de remediation posées (opt-in : rien sans politique)"}},"tags":["securite"]},"put":{"operationId":"poser_remediation_config","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemediationConfigReq"}}},"required":true},"responses":{"200":{"description":"Politiques posées — le plan (/config/remediation/plan) les appliquera à la prochaine évaluation"},"400":{"description":"Une politique est invalide (nommée)"}},"tags":["securite"]}},"/v1/projects/{pid}/config/remediation/plan":{"get":{"operationId":"plan_remediation_config","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Actions concrètes planifiées depuis la dernière évaluation persistée (rien pour les règles sans politique)"}},"tags":["securite"]}},"/v1/projects/{pid}/consommateurs":{"get":{"operationId":"lister_consommateurs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Consommateurs de l'org (préfixe + quota, jamais la clé)"}},"tags":["passerelle"]},"post":{"operationId":"creer_consommateur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewConsommateurReq"}}},"required":true},"responses":{"201":{"description":"Consommateur créé — clé complète affichée UNE SEULE FOIS"}},"tags":["passerelle"]}},"/v1/projects/{pid}/consommateurs/{cid}":{"delete":{"operationId":"supprimer_consommateur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Consommateur supprimé — sa clé meurt immédiatement"}},"tags":["passerelle"]},"get":{"operationId":"detail_consommateur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail + usage de la fenêtre courante"}},"tags":["passerelle"]},"put":{"operationId":"maj_consommateur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajConsommateurReq"}}},"required":true},"responses":{"200":{"description":"Consommateur modifié (nom, quota, actif)"}},"tags":["passerelle"]}},"/v1/projects/{pid}/consommateurs/{cid}/regenerer":{"post":{"operationId":"regenerer_cle_consommateur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Nouvelle clé générée — l'ancienne est morte (affichée une seule fois)"}},"tags":["passerelle"]}},"/v1/projects/{pid}/courrier":{"get":{"operationId":"lister_courrier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Courriel sortant — états réels, relais configuré"}},"tags":["automatisation"]},"post":{"operationId":"envoyer_courrier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewCourrierEtenduReq"}}},"required":true},"responses":{"201":{"description":"Message envoyé au relais SMTP — ou échec honnête avec la raison ; les pièces voyagent en multipart/mixed"}},"tags":["automatisation"]}},"/v1/projects/{pid}/courrier/retries":{"get":{"operationId":"lister_retries_courrier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"File de retries : échéances mesurées, essais historiques, cycles épuisés SIGNALÉS"}},"tags":["automatisation"]}},"/v1/projects/{pid}/courrier/retries/{mid}/rejouer":{"post":{"operationId":"rejouer_retry_courrier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Nouveau cycle de tentatives lancé (essai immédiat) — l'historique mesuré est conservé"},"404":{"description":"Message hors de la file de retries"}},"tags":["automatisation"]}},"/v1/projects/{pid}/courrier/templates":{"get":{"operationId":"lister_templates_courrier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Templates à variables de l'org"}},"tags":["automatisation"]},"post":{"operationId":"creer_template_courrier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateCourrielReq"}}},"required":true},"responses":{"201":{"description":"Template créé (validé : au moins une variable, bornes)"},"400":{"description":"Template invalide (nommé)"}},"tags":["automatisation"]}},"/v1/projects/{pid}/courrier/templates/{tid}/rendre":{"post":{"operationId":"rendre_template_courrier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RendreTemplateReq"}}},"required":true},"responses":{"200":{"description":"Sujet (UNE ligne, assaini) et corps rendus"},"400":{"description":"Variable sans valeur (nommée) ou injection d'en-tête neutralisée"},"404":{"description":"Template inconnu"}},"tags":["automatisation"]}},"/v1/projects/{pid}/courrier/webhooks":{"get":{"operationId":"lister_webhooks_delivery","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Inscriptions (le secret n'est JAMAIS rendu après la création) + derniers événements journalisés"}},"tags":["notifications"]},"post":{"operationId":"creer_webhook_delivery","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewWebhookDeliveryReq"}}},"required":true},"responses":{"201":{"description":"Inscription créée — le SECRET PARTAGÉ (HMAC-SHA256) n'est rendu qu'ICI, une seule fois"}},"tags":["notifications"]}},"/v1/projects/{pid}/courrier/webhooks/{wid}":{"delete":{"operationId":"supprimer_webhook_delivery","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Inscription supprimée (le secret part avec)"},"404":{"description":"Inscription inconnue"}},"tags":["notifications"]},"put":{"operationId":"maj_webhook_delivery","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajWebhookDeliveryReq"}}},"required":true},"responses":{"200":{"description":"Inscription mise à jour (activer/désactiver, genres suivis)"},"404":{"description":"Inscription inconnue"}},"tags":["notifications"]}},"/v1/projects/{pid}/courrier/webhooks/{wid}/evenement":{"post":{"operationId":"evenement_webhook_delivery","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Événement VÉRIFIÉ HMAC (corps brut + X-Webhook-Signature) puis journalisé et NOTIFIÉ aux webhooks actifs qui suivent le genre"},"401":{"description":"Signature absente ou invalide"},"404":{"description":"Inscription inconnue"}},"tags":["notifications"]}},"/v1/projects/{pid}/courrier/{mid}":{"delete":{"operationId":"supprimer_courrier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Message retiré du registre"}},"tags":["automatisation"]},"get":{"operationId":"detail_courrier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'un courriel — état, tentatives, erreur éventuelle, pièces"}},"tags":["automatisation"]}},"/v1/projects/{pid}/courrier/{mid}/renvoyer":{"post":{"operationId":"renvoyer_courrier","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Nouvel essai réel vers le relais — échec honnête si toujours injoignable"}},"tags":["automatisation"]}},"/v1/projects/{pid}/couts/anomalies":{"get":{"operationId":"lister_anomalies_couts","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"=1 : chaque anomalie rend aussi ses corrélations d'événements (fenêtre 7 jours)","in":"query","name":"expliquer","required":false,"schema":{"format":"int32","minimum":0,"type":"integer"}},{"description":"fenêtre de corrélation en jours (défaut 7, borne 1-30)","in":"query","name":"fenetre_jours","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"description":"Historique quotidien, baseline, évaluation courante et anomalies (+ explications si ?expliquer=1)"}},"summary":"Explication des anomalies de coûts par les événements (W3-2) :\nchaque anomalie est confrontée aux événements créateurs/destructeurs\nde ressources des N jours précédents — corrélation datée, jamais\ncausale par défaut.","tags":["couts"]}},"/v1/projects/{pid}/couts/anomalies/isolation-forest":{"post":{"operationId":"isolation_forest_anomalies","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IsolationForestReq"}}},"required":true},"responses":{"200":{"description":"Scores de TOUS les points (indice, score 1..=2, triés DÉCROISSANT — un point isolé vite score vers 2) + les indices TOP franchissant le seuil ; DÉTERMINISTE : mêmes points + même graine ⇒ mêmes scores"},"400":{"description":"ISOLATION_REFUSEE — aucun point, dimensions incohérentes, zéro arbre, sous-échantillon nul, valeurs non finies, ou seuil hors 1..=2"}},"tags":["couts"]}},"/v1/projects/{pid}/couts/comparatif":{"get":{"operationId":"comparatif_periodes_couts","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"période de référence AAAA-MM (défaut : mois précédent)","in":"query","name":"avant","required":false,"schema":{"type":"string"}},{"description":"période comparée AAAA-MM (défaut : mois courant)","in":"query","name":"apres","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deltas par famille + total, familles triées par écart absolu"},"404":{"description":"Aucune mesure pour une période demandée (ni facture émise, ni mois courant)"}},"summary":"Comparatif de deux périodes (FLUX W-11) : deltas exacts par famille\n(hausse/rouge, baisse/vert, stable/neutre), triés par écart absolu\ndécroissant — chaque montant vient d'une facture mesurée, jamais\nextrapolé. Défauts : mois précédent → mois courant.","tags":["couts"]}},"/v1/projects/{pid}/couts/devis":{"post":{"operationId":"construire_devis_couts","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DevisReq"}}},"required":true},"responses":{"201":{"description":"Devis complet : lignes triées par coût mensuel, totaux h/j/mois, gabarits inconnus nommés"},"400":{"description":"Plus de 50 lignes demandées"}},"summary":"Devis JSON (FLUX W-11) : tarifs et dimensions EXCLUSIVEMENT du\ncatalogue — un gabarit inconnu est nommé, jamais prix. Le document\nrendu EST l'export (relu tel quel par le module, aucune double\nreprésentation).","tags":["couts"]}},"/v1/projects/{pid}/couts/evaluer":{"post":{"operationId":"evaluer_couts","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Photo du jour enregistrée + évaluation exécutée (anomalie éventuelle, dédupliquée)"}},"tags":["couts"]}},"/v1/projects/{pid}/couts/grille":{"get":{"operationId":"grille_couts_periodes","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"liste AAAA-MM séparée par des virgules (défaut : 3 derniers mois)","in":"query","name":"periodes","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Grille familles × périodes, totaux par période"},"404":{"description":"Une période demandée n'a aucune mesure"}},"summary":"Grille familles × périodes (W3-1) : N périodes croisées, chaque\ncase MESURÉE (facture émise, ou mois courant vivant) — une période\nsans mesure rend un 404 honnête, la grille n'invente pas de colonne.","tags":["couts"]}},"/v1/projects/{pid}/couts/prevision":{"get":{"operationId":"prevision_fin_mois_couts","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Projection du mois courant : cumul mesuré, rythme moyen, bornes min/max — `suffisant=false` dit quand il n'y a pas assez de jours mesurés pour extrapoler"}},"summary":"Prévision de fin de mois (W3-1) : projection centrale + fourchette\nhonnête depuis les photos journalières MESURÉES du showback (la\nmême histoire que les anomalies de coûts — rien n'est recalculé).","tags":["couts"]}},"/v1/projects/{pid}/couts/ventilation":{"get":{"operationId":"ventiler_couts_par_tag","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"clé de ventilation (ex. projet, equipe, cost-center)","in":"query","name":"cle","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Postes triés par coût décroissant + sans-tag nommé"},"400":{"description":"Clé de ventilation absente/invalide"}},"summary":"Ventilation de la facture vivante par tag (W3-1) : chaque poste est\nune somme exacte de lignes MESURÉES (mêmes tarifs que la facture),\nles ressources sans la clé sont NOMMÉES.","tags":["couts"]}},"/v1/projects/{pid}/crons":{"get":{"operationId":"lister_crons","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["automatisation"]},"post":{"operationId":"creer_cron","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewCronReq"}}},"required":true},"responses":{"201":{"description":"Créé"},"404":{"description":"Cible inexistante (serveur_id/machine_id)"},"422":{"description":"commande exige serveur_id ou machine_id (et une seule cible)"}},"tags":["automatisation"]}},"/v1/projects/{pid}/crons/fenetre-maintenance":{"get":{"operationId":"lire_fenetre_maintenance","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Fenêtre de maintenance posée (null si aucune) + prochain report effectif"}},"tags":["automatisation"]},"put":{"operationId":"poser_fenetre_maintenance","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FenetreMaintenanceReq"}}},"required":true},"responses":{"200":{"description":"Fenêtre posée — les tirages dans la plage seront reportés"}},"tags":["automatisation"]}},"/v1/projects/{pid}/crons/{cid}":{"delete":{"operationId":"supprimer_cron","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["automatisation"]}},"/v1/projects/{pid}/crons/{cid}/trigger":{"post":{"operationId":"executer_cron","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Déclenché — la commande (si posée) est exécutée sur sa cible et la trace (code_sortie/stdout/stderr tronqués) est retournée et persistée"},"404":{"description":"Cron introuvable"}},"summary":"Déclenche un cron à la demande (le scheduler le fait sinon à l'heure).","tags":["automatisation"]}},"/v1/projects/{pid}/databases":{"get":{"operationId":"lister_databases","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["donnees"]},"post":{"operationId":"creer_database","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewDatabaseReq"}}},"required":true},"responses":{"201":{"description":"Créé. Moteur ferrodb : VRAI pod FerroDB sur le cluster plateforme (Deployment + Service NodePort + PVC à la taille du plan + netpol dédiée), utilisateur pgwire créé DANS l'instance (CREATE USER + GRANT ALL), host-record dnsmasq db-<hash>.cirrostratus.internal → IP du nœud, URI fonctionnelle de bout en bout ; rollback complet si une étape échoue. Plans db-ha/db-custom : provisionnés mono-nœud sur k8s (multi-nœuds à venir), taille/PVC du plan appliqués. CIRRO_WORKLOADS_REELS=0 (dev/CI/e2e) : registre seul, dit honnêtement. Moteur postgres : instance PostgreSQL réelle (initdb + pg_ctl)."}},"tags":["donnees"]}},"/v1/projects/{pid}/databases/{did}":{"delete":{"operationId":"supprimer_database","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé — moteur ferrodb : DROP USER dans l'instance, pod FerroDB retiré du cluster (deploy, svc, PVC, netpol dédiée), host-record dnsmasq retiré ; tolérant aux pièces injoignables (tracé dans l'audit)"}},"tags":["donnees"]},"get":{"operationId":"detail_database","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["donnees"]}},"/v1/projects/{pid}/databases/{did}/import-analyse":{"post":{"operationId":"analyser_import_pg_dump","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportAnalyseReq"}}},"required":true},"responses":{"200":{"description":"Rapport d'import : tout est compté + limites documentées du parseur"}},"tags":["donnees"]}},"/v1/projects/{pid}/dataflows":{"get":{"operationId":"lister_dataflows","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pipelines ETL de l'org — tables etl_ matérialisées"}},"tags":["donnees"]},"post":{"operationId":"creer_dataflow","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewDataflowReq"}}},"required":true},"responses":{"201":{"description":"Pipeline ETL créé — exécution réelle à la demande ou planifiée"}},"tags":["donnees"]}},"/v1/projects/{pid}/dataflows/lineage":{"get":{"operationId":"lineage_dataflows","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"table cible : rend son amont et son aval en plus du graphe","in":"query","name":"table","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Graphe sources → etl_ → rapports (+ amont/aval de ?table=)"}},"tags":["donnees"]}},"/v1/projects/{pid}/dataflows/multi":{"get":{"operationId":"lister_dataflows_multi","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pipelines multi-sources de l'org (N extractions, jointure par clé, étapes ordonnées)"}},"tags":["donnees"]},"post":{"operationId":"creer_dataflow_multi","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineMultiReq"}}},"required":true},"responses":{"201":{"description":"Pipeline multi-sources créé (validé : sources, clés de jointure, étapes)"},"400":{"description":"PIPELINE_INVALIDE — chaque manquement est nommé"},"409":{"description":"DEJA_PRIS — un pipeline porte déjà ce nom"}},"tags":["donnees"]}},"/v1/projects/{pid}/dataflows/multi/{nom}":{"delete":{"operationId":"supprimer_dataflow_multi","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pipeline supprimé"},"404":{"description":"Pipeline inconnu"}},"tags":["donnees"]},"put":{"operationId":"maj_dataflow_multi","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineMultiReq"}}},"required":true},"responses":{"200":{"description":"Pipeline remplacé (validé à nouveau)"},"404":{"description":"Pipeline inconnu"}},"tags":["donnees"]}},"/v1/projects/{pid}/dataflows/multi/{nom}/etapes-conteneur":{"get":{"operationId":"lire_etapes_conteneur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Étapes conteneurisées posées + COMMANDES EXACTES rendues (jamais exécutées ici)"},"404":{"description":"Pipeline inconnu"}},"tags":["donnees"]},"put":{"operationId":"poser_etapes_conteneur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EtapesConteneurReq"}}},"required":true},"responses":{"200":{"description":"Étapes remplacées (image étiquetée, contrat de sortie vérifiable) — la COMMANDE exacte est rendue, jamais exécutée ici"},"400":{"description":"ETAPE_INVALIDE — chaque manquement nommé"},"404":{"description":"Pipeline inconnu"}},"tags":["donnees"]}},"/v1/projects/{pid}/dataflows/multi/{nom}/executer":{"post":{"operationId":"executer_dataflow_multi","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuterMultiReq"}}},"required":true},"responses":{"200":{"description":"Exécution MESURÉE : lignes jointes + chaque écart compté (sans clé, sans correspondance, filtrées)"},"400":{"description":"EXECUTION_REFUSEE — donnée manquante pour une source, schéma incohérent"},"404":{"description":"Pipeline inconnu"}},"tags":["donnees"]}},"/v1/projects/{pid}/dataflows/multi/{nom}/micro-lots":{"get":{"operationId":"lire_micro_lots","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Journal des micro-lots + statistiques MESURÉES (latences dérivées des horodatages, retard cumulé)"},"404":{"description":"Pipeline inconnu"}},"tags":["donnees"]},"put":{"operationId":"poser_micro_lots","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MicroLotsReq"}}},"required":true},"responses":{"200":{"description":"Planification posée (intervalle borné 1 s - 24 h), journal vide"},"400":{"description":"INTERVALLE_INVALIDE — bornes 1-86400 s"},"404":{"description":"Pipeline inconnu"}},"tags":["donnees"]}},"/v1/projects/{pid}/dataflows/multi/{nom}/micro-lots/executions":{"post":{"operationId":"inscrire_execution_micro_lot","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecutionMicroLotReq"}}},"required":true},"responses":{"200":{"description":"Exécution inscrite (journal borné) + latence et retard DÉRIVÉS des horodatages fournis"},"404":{"description":"Pipeline inconnu ou sans planification en micro-lots"}},"tags":["donnees"]}},"/v1/projects/{pid}/dataflows/{fid}":{"delete":{"operationId":"supprimer_dataflow","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pipeline supprimé ET sa table etl_ détruite"}},"tags":["donnees"]},"get":{"operationId":"detail_dataflow","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'un pipeline — journal d'exécutions borné"}},"tags":["donnees"]},"put":{"operationId":"maj_dataflow","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajDataflowReq"}}},"required":true},"responses":{"200":{"description":"Pipeline modifié — source, transformation, planification, pause"}},"tags":["donnees"]}},"/v1/projects/{pid}/dataflows/{fid}/executer":{"post":{"operationId":"executer_dataflow_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Exécution réelle : source extraite, table etl_ (re)matérialisée, lignes comptées"}},"tags":["donnees"]}},"/v1/projects/{pid}/dataflows/{fid}/exporter":{"get":{"operationId":"exporter_dataflow_csv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"CSV de la dernière table matérialisée (text/csv)"}},"tags":["donnees"]}},"/v1/projects/{pid}/dedies":{"get":{"operationId":"lire_dedies","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Hôtes réservés + affinités (capacité garantie)"}},"tags":["compute"]},"post":{"operationId":"creer_dedie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewHoteReq"}}},"required":true},"responses":{"201":{"description":"Hôte réservé (affinité VM→hôte, prix dérivé)"}},"tags":["compute"]}},"/v1/projects/{pid}/dedies/migration/plan":{"post":{"operationId":"plan_migration_dedie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanMigrationReq"}}},"required":true},"responses":{"200":{"description":"Plan de live-migration : ordre par charge MESURÉE croissante (non mesurées en fin, égalités alphabétiques), interruption = durée MESURÉE de chaque VM, cycle validé contre la fenêtre"},"400":{"description":"PLAN_REFUSE — VM sans durée mesurée (aucune estimation ne sera inventée), charge impossible, cycle plus long que la fenêtre, raisons nommées"}},"tags":["calcul"]}},"/v1/projects/{pid}/dedies/profils":{"get":{"operationId":"profils_hotes_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Catalogue des profils d'hôtes : capacités DÉCLARÉES (l'inventaire cirroHosts prime toujours à la réservation), prix horaire DÉRIVÉ du tarif vCPU — jamais saisi"}},"tags":["calcul"]}},"/v1/projects/{pid}/dedies/recommander":{"post":{"operationId":"recommander_dedie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiniteReq"}}},"required":true},"responses":{"200":{"description":"Hôte réservé le plus libre après placement"}},"tags":["compute"]}},"/v1/projects/{pid}/dedies/{hid}":{"delete":{"operationId":"supprimer_dedie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"hid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Hôte réservé retiré (affinités orphelines signalées avant)"}},"tags":["compute"]}},"/v1/projects/{pid}/dedies/{hid}/affinites":{"post":{"operationId":"poser_affinite","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"hid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiniteReq"}}},"required":true},"responses":{"200":{"description":"VM posée sur l'hôte réservé (capacité vérifiée)"}},"tags":["compute"]}},"/v1/projects/{pid}/dedies/{hid}/affinites/{vm}":{"delete":{"operationId":"retirer_affinite_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"hid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vm","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Affinité retirée"}},"tags":["compute"]}},"/v1/projects/{pid}/depots":{"get":{"operationId":"lister_depots","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Dépôts hors-ligne de l'org + catalogue des genres + espace NAS"}},"tags":["depots"]},"post":{"operationId":"creer_depot","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewDepotReq"}}},"required":true},"responses":{"201":{"description":"Dépôt créé — dossier prêt sur le NAS, prêt à synchroniser"}},"tags":["depots"]}},"/v1/projects/{pid}/depots/harbor":{"get":{"operationId":"offre_harbor","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom DNS sous lequel le registre répond (ex. registry.nantes.cirro)","in":"query","name":"hote","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Offre Harbor : fonctions apportées, dépendances système, plan de commandes de l'installeur officiel (VÉRIFIÉ ni quote ni pipe) — jamais exécuté"},"400":{"description":"HOTE_REQUIS — le registre ne répond pas sur « rien »"}},"tags":["depots"]}},"/v1/projects/{pid}/depots/replications":{"get":{"operationId":"lister_replications","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Réplications avec état de divergence DÉRIVÉ des seuls digests mesurés + celles à répliquer"}},"tags":["depots"]},"post":{"operationId":"creer_replication","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewReplicationReq"}}},"required":true},"responses":{"201":{"description":"Descripteur créé (les digests fournis sont validés canoniques ou refusés)"}},"tags":["depots"]}},"/v1/projects/{pid}/depots/replications/{rid}/digests":{"post":{"operationId":"maj_digests_replication","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajDigestsReq"}}},"required":true},"responses":{"200":{"description":"Digests MESURÉS enregistrés + divergence redérivée (skopeo inspect exécuté par cirrod, pas ici)"},"404":{"description":"Réplication inconnue"}},"tags":["depots"]}},"/v1/projects/{pid}/depots/{did}":{"delete":{"operationId":"supprimer_depot","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Dépôt supprimé — dossier NAS retiré"}},"tags":["depots"]}},"/v1/projects/{pid}/depots/{did}/scan-trivy":{"post":{"operationId":"plan_scan_trivy_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanScanTrivyReq"}}},"required":true},"responses":{"200":{"description":"PLAN de la commande trivy EXACTE (jamais exécutée ici — cirrod l'exécutera avec internet)"}},"tags":["depots"]}},"/v1/projects/{pid}/depots/{did}/scan-trivy/rapport":{"post":{"operationId":"rapport_scan_trivy_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RapportTrivyReq"}}},"required":true},"responses":{"200":{"description":"Sévérités comptées sur le rapport RÉEL + décision de poussée (en garde de la synchronisation du dépôt)"},"400":{"description":"Rapport illisible ou seuil inconnu"},"409":{"description":"Synchronisation bloquée : vulnérabilités au niveau du seuil"}},"tags":["depots"]}},"/v1/projects/{pid}/depots/{did}/synchroniser":{"post":{"operationId":"synchroniser_depot","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Synchronisation lancée en tâche de fond — suivez l'état du dépôt"}},"tags":["depots"]}},"/v1/projects/{pid}/dns/resoudre":{"post":{"operationId":"resoudre_dns","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResoudreReq"}}},"required":true},"responses":{"200":{"description":"cirroDNS : résolution par le moteur (même code que le serveur UDP)"}},"tags":["reseau"]}},"/v1/projects/{pid}/dns/zone-inverse":{"post":{"operationId":"poser_zone_inverse","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneInverseReq"}}},"required":true},"responses":{"201":{"description":"Zone arpa créée + PTRs posés (validés par les règles cirroDNS)"},"400":{"description":"CIDR non aligné octet, TTL hors bornes…"},"404":{"description":"Réseau inconnu"},"409":{"description":"La zone arpa existe déjà (dans l'org ou ailleurs)"}},"tags":["reseau"]}},"/v1/projects/{pid}/dns/zone-inverse/{rid}":{"get":{"operationId":"lire_zone_inverse","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"identifiant du réseau","in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Enregistrements reverse régénérés de l'IPAM à l'instant (aucune pose)"},"404":{"description":"Réseau inconnu"}},"tags":["reseau"]}},"/v1/projects/{pid}/dns/zones":{"get":{"operationId":"lister_zones_dns","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"cirroDNS : zones DNS de l'organisation"}},"tags":["reseau"]},"post":{"operationId":"creer_zone_dns","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewZoneReq"}}},"required":true},"responses":{"201":{"description":"cirroDNS : zone créée (ex. lab.cirrostratus.internal)"}},"tags":["reseau"]}},"/v1/projects/{pid}/dns/zones/{zid}":{"delete":{"operationId":"supprimer_zone_dns","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"zid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Zone (et ses enregistrements) supprimée"}},"tags":["reseau"]}},"/v1/projects/{pid}/dns/zones/{zid}/politique-route":{"get":{"operationId":"lire_politique_route_dns","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"zid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Politique de routage géo/latence de la zone (pools régionaux, repli) — absente : dit tel quel"},"404":{"description":"Zone inconnue"}},"tags":["reseau"]},"put":{"operationId":"poser_politique_route_dns","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"zid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolitiqueRouteDnsReq"}}},"required":true},"responses":{"200":{"description":"Politique posée (genre, régions uniques, cibles IPv4, CIDR sources lisibles, repli connu) — le nom de zone est pris de la ZONE, pas du corps"},"400":{"description":"POLITIQUE_INVALIDE — chaque manquement nommé"},"404":{"description":"Zone inconnue"}},"tags":["reseau"]}},"/v1/projects/{pid}/dns/zones/{zid}/politique-route/resoudre":{"post":{"operationId":"resoudre_route_dns","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"zid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResoudreRouteDnsReq"}}},"required":true},"responses":{"200":{"description":"Décision DÉTERMINISTE : région choisie (geo = CIDR couvrant, latence = RTT mesuré le plus bas), cible par hachage documenté, EMPREINTE rendue (auditable)"},"400":{"description":"RESOLUTION_REFUSEE — aucune région ne couvre la source, tout muet, latence jamais mesurée"},"404":{"description":"Zone inconnue ou sans politique"}},"tags":["reseau"]}},"/v1/projects/{pid}/dns/zones/{zid}/records":{"get":{"operationId":"lister_enregistrements","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"zid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Enregistrements de la zone"}},"tags":["reseau"]},"post":{"operationId":"creer_enregistrement","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"zid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRecordReq"}}},"required":true},"responses":{"201":{"description":"Enregistrement ajouté (A, AAAA, CNAME, TXT)"}},"tags":["reseau"]}},"/v1/projects/{pid}/dns/zones/{zid}/records/{rid}":{"delete":{"operationId":"supprimer_enregistrement","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"zid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Enregistrement supprimé"}},"tags":["reseau"]},"put":{"operationId":"modifier_enregistrement","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"zid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRecordReq"}}},"required":true},"responses":{"200":{"description":"Enregistrement modifié (valeur/TTL/genre/nom) — les records gérés par cirroCarte sont refusés"}},"tags":["reseau"]}},"/v1/projects/{pid}/ecarts":{"get":{"operationId":"ecarts_estimation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"P4-16 — Écarts estimation/réalité (CU-15) : compare le €/mois estimé à\nla création au coût constaté (tarif × temps de vie). Fourchette ±20 %.","tags":["automatisation"]}},"/v1/projects/{pid}/edge/{tid}":{"get":{"operationId":"etat_edge","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Identifiant du thing","in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"État du runtime edge : EN_LIGNE / SYNCHRONISE / HORS_LIGNE, file locale, compteurs MESURÉS (délivrées, empilées, refusées, rejouées, idempotentes, divergences)"},"404":{"description":"Thing inconnu de l'org"}},"tags":["iot"]}},"/v1/projects/{pid}/edge/{tid}/actions":{"post":{"operationId":"agir_edge","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionEdgeReq"}}},"required":true},"responses":{"200":{"description":"DELIVREE (connecté) ou EN_FILE (hors ligne) — file pleine : REFUS nommé, jamais de perte silencieuse"},"400":{"description":"ACTION_REFUSEE — clé/genre/charge hors contrat"},"404":{"description":"Thing inconnu"}},"tags":["iot"]}},"/v1/projects/{pid}/edge/{tid}/connexion":{"post":{"operationId":"connexion_edge","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnexionEdgeReq"}}},"required":true},"responses":{"200":{"description":"Transition de connectivité appliquée — déjà hors ligne / déjà connecté : refus nommé (rien ne se rafale)"},"400":{"description":"ACTION_INVALIDE / TRANSITION_REFUSEE"},"404":{"description":"Thing inconnu"}},"tags":["iot"]}},"/v1/projects/{pid}/edge/{tid}/delta":{"post":{"operationId":"delta_edge","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Thing edge","in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeltaEdgeReq"}}},"required":true},"responses":{"200":{"description":"Delta par empreintes de chunks — seuls les chunks inconnus partent, économie mesurée"}},"tags":["iot"]}},"/v1/projects/{pid}/edge/{tid}/historique":{"get":{"operationId":"historique_edge_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Historique RÉEL des versions posées (chronique, rollback inclus : origine rollback:echec_ack)"},"404":{"description":"Thing inconnu ou jamais déployé"}},"tags":["iot"]}},"/v1/projects/{pid}/edge/{tid}/rejeu":{"post":{"operationId":"rejouer_edge","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Thing edge","in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejeuEdgeReq"}}},"required":true},"responses":{"200":{"description":"Rejeu FIFO idempotent — divergence nommée qui arrête le rejeu, état constaté"}},"tags":["iot"]}},"/v1/projects/{pid}/edge/{tid}/rollback":{"post":{"operationId":"rollback_edge","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Thing edge","in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RollbackEdgeReq"}}},"required":true},"responses":{"200":{"description":"Rollback sur ACK d'échec mesuré — N-1 = historique réel du device, jamais arithmétique"}},"tags":["iot"]}},"/v1/projects/{pid}/equivalents/apply":{"post":{"operationId":"apply_equivalents_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanIacReq"}}},"required":true},"responses":{"200":{"description":"Déroule le plan calculé contre l'existant réel : les SUPPRESSIONS de VMs et volumes sont des VRAIES mutations (helpers du registre, arrêt au PREMIER échec avec rapport partiel) ; créations et modifications sont SIMULÉES en le DISANT (le plan ne porte ni image ni zone pour créer, aucun helper de mutation type/taille n'existe) — un apply échoué ne rend jamais « tout OK »"},"400":{"description":"PLAN_REFUSE — plus de 200 lignes, famille ou id vide"},"409":{"description":"APPLY_ARRETE — une suppression réelle a échoué : le rapport partiel (réussites comptées par genre) et l'écart fautif sont cités, les écarts restants ne sont PAS soumis"}},"tags":["equivalents"]}},"/v1/projects/{pid}/equivalents/couverture":{"get":{"operationId":"couverture_provider","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Croisement MESURÉ des familles créatrices contre les resources cirro_* du provider — la dette est rendue comme dette, avec le gabarit générique recommandé"}},"tags":["general"]}},"/v1/projects/{pid}/equivalents/modules/{pattern}":{"get":{"operationId":"module_tofu_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"app-base | stack-complete | evenements","in":"path","name":"pattern","required":true,"schema":{"type":"string"}},{"description":"app-base/stack-complete : préfixe de nommage [a-z0-9-] 1-30","in":"query","name":"suffixe","required":true,"schema":{"type":"string"}},{"description":"app-base/stack-complete : image conteneur ÉTIQUETÉE (jamais :latest)","in":"query","name":"image","required":true,"schema":{"type":"string"}},{"description":"Port d'écoute 1-65535 (défaut 8080)","in":"query","name":"port","required":true,"schema":{"format":"int64","type":"integer"}},{"description":"Nœuds FerroDB : 1, 3 ou 5 (défaut 1)","in":"query","name":"noeuds","required":true,"schema":{"format":"int64","type":"integer"}},{"description":"stack-complete : nom de la zone DNS à créer","in":"query","name":"zone","required":true,"schema":{"type":"string"}},{"description":"stack-complete : label posé sur la zone","in":"query","name":"sous_domaine","required":true,"schema":{"type":"string"}},{"description":"stack-complete : ajouter cirroCDN (vrai|faux)","in":"query","name":"cdn","required":true,"schema":{"type":"boolean"}},{"description":"stack-complete : TTL des objets CDN 1-86400 s","in":"query","name":"ttl_cdn_s","required":true,"schema":{"format":"int64","type":"integer"}},{"description":"evenements : nom du topic","in":"query","name":"topic","required":true,"schema":{"type":"string"}},{"description":"evenements : préfixe de sujet filtré (vide = tout)","in":"query","name":"filtre","required":true,"schema":{"type":"string"}},{"description":"evenements : URL du webhook abonné","in":"query","name":"url_webhook","required":true,"schema":{"type":"string"}},{"description":"evenements : nom de la fonction consommatrice","in":"query","name":"fonction","required":true,"schema":{"type":"string"}},{"description":"evenements : python | nodejs","in":"query","name":"langage","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Le pattern complet : variables.tf + main.tf + outputs.tf + main.tf d'appel — bornes validées côté Rust AVANT génération, les blocs validation HCL re-vérifient au plan"},"400":{"description":"PARAMETRES_INVALIDES — chaque borne franchie est nommée"},"404":{"description":"Pattern inconnu"}},"tags":["general"]}},"/v1/projects/{pid}/equivalents/plan":{"post":{"operationId":"plan_equivalents_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanIacReq"}}},"required":true},"responses":{"200":{"description":"Diff du souhaité contre l'EXISTANT RÉEL de l'org (VMs + volumes MESURÉS du registre) : +à créer, -à supprimer, ~UN champ nommé à modifier (nom, type, taille — le premier divergent) ; le plan lisible est RENDU, jamais exécuté"},"400":{"description":"PLAN_REFUSE — plus de 200 lignes, famille ou id vide"}},"tags":["equivalents"]}},"/v1/projects/{pid}/equivalents/tf-import":{"post":{"operationId":"generer_tf_import","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Inventaire sérialisé des stores RÉELS de l'org → main.tofu avec blocs import (id réels) — les secrets ne portent JAMAIS de valeur (var.sensible_*), l'import partiel est DIT famille par famille"},"500":{"description":"STORE_ILLISIBLE — un store n'a pas répondu : l'inventaire refuse, il ne devine pas"}},"tags":["general"]}},"/v1/projects/{pid}/estimate":{"get":{"operationId":"estimer_projet","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["compute"]}},"/v1/projects/{pid}/estimateur/architecture":{"post":{"operationId":"simuler_architecture_couts","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulArchReq"}}},"required":true},"responses":{"200":{"description":"Devis simulé : ventilation par famille au TARIF DU CATALOGUE × quantité, totaux €/h et €/mois (× 720 h) SANS et AVEC réservation (remise existante de cirroReservations, table DUREES — jamais réinventée)"},"400":{"description":"SIMULATION_REFUSEE — gabarit inconnu du catalogue (cité), plus de 100 lignes"}},"tags":["couts"]}},"/v1/projects/{pid}/events":{"get":{"operationId":"sse_events","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Flux SSE des evenements de lorg"}},"tags":["systeme"]}},"/v1/projects/{pid}/export":{"get":{"operationId":"exporter_org","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"1 = résumé seul (léger, non audité)","in":"query","name":"resume","required":false,"schema":{"type":"string"}},{"description":"donnees = objets S3 + secrets chiffrés dans le bundle (v2) ; défaut = configuration seule","in":"query","name":"mode","required":false,"schema":{"type":"string"}},{"description":"chiffre les valeurs de secrets (AES-256-GCM, clé dérivée Argon2id) — requise pour que les valeurs partent ; jamais journalisée","in":"query","name":"passphrase","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Bundle d'export signé (anti-lock-in)"},"400":{"description":"Mode inconnu ou passphrase vide"},"503":{"description":"Passerelle S3 injoignable (mode donnees) — aucun inventaire silencieux"}},"tags":["iam"]}},"/v1/projects/{pid}/facture":{"get":{"operationId":"facture_org_courante","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lignes de facture exhaustive du mois + total €/mois"}},"summary":"Lignes de facture mensuelle d'une org, DÉRIVÉES de l'usage réel\n(mêmes tarifs showback que /showback — jamais de chiffre inventé).\n0.109 : facture exhaustive du mois, vue par l'ORG elle-même\n(project.read) — le même cœur `lignes_facture_org` que la facture\nsuperadmin et la vue-ensemble ; alimente l'outil agents `facture`\net MCP cirro_facture. Jamais une seconde formule.","tags":["systeme"]}},"/v1/projects/{pid}/features":{"get":{"operationId":"lister_features","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Groupes de features de l'org"}},"tags":["general"]},"post":{"operationId":"creer_feature_groupe","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewGroupeFeaturesReq"}}},"required":true},"responses":{"201":{"description":"Groupe créé — son schéma est figé à la création"}},"tags":["general"]}},"/v1/projects/{pid}/features/en-ligne/publier":{"post":{"operationId":"publier_en_ligne","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublierEnLigneReq"}}},"required":true},"responses":{"200":{"description":"Entrée ENTIÈRE publiée pour (groupe, entité) — remplace la précédente, l'instant de calcul devient la référence de fraîcheur"},"400":{"description":"PUBLICATION_REFUSEE — bornes (features, entités, textes)"}},"tags":["donnees"]}},"/v1/projects/{pid}/features/en-ligne/{groupe}/{entite}":{"get":{"operationId":"lire_en_ligne","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"groupe","required":true,"schema":{"type":"string"}},{"in":"path","name":"entite","required":true,"schema":{"type":"string"}},{"description":"Features demandées, séparées par des virgules (ex. panier_moyen,visites)","in":"query","name":"features","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Sous-ensemble demandé, servi TEL QUE STOCKÉ, fraîcheur MESURÉE (maintenant − dernier calcul)"},"400":{"description":"LECTURE_REFUSEE — entrée expirée (fraîcheur citée) ou feature absente : refus nommé, jamais déduit"},"404":{"description":"Groupe/entité inconnus du magasin en ligne"}},"tags":["donnees"]}},"/v1/projects/{pid}/features/flux/definitions":{"get":{"operationId":"lire_definitions_flux_features","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Définitions déclarées + compteurs MESURÉS de l'ingesteur (événements, recalculs, fenêtres vides, retards)"}},"tags":["donnees"]},"put":{"operationId":"poser_definitions_flux_features","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DefinitionFeature"},"type":"array"}}},"required":true},"responses":{"200":{"description":"Définitions posées (corps = définitions du noyau features, non portées au schéma OpenAPI)"}},"tags":["features"]}},"/v1/projects/{pid}/features/flux/ingester":{"post":{"operationId":"ingerer_evenement_features","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngesterFluxReq"}}},"required":true},"responses":{"200":{"description":"Événement ingéré : features dépendantes recalculées sur leur fenêtre et republiées ; fenêtres vides NOMMÉES ; retard MESURÉ"},"400":{"description":"INGESTION_REFUSEE — entité/source hors bornes, ou publication refusée par le magasin"},"409":{"description":"AUCUNE_DEFINITION — l'ingesteur n'écoute rien (posez les définitions d'abord)"}},"tags":["donnees"]}},"/v1/projects/{pid}/features/groupes":{"get":{"operationId":"lister_groupes_features","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Groupes de features : schéma figé + entités versionnées (bornes capacités respectées)"}},"tags":["donnees"]},"post":{"operationId":"creer_groupe_features","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreerGroupeFeaturesReq"}}},"required":true},"responses":{"201":{"description":"Groupe créé — le schéma (liste de features) est FIGÉ à la création"},"400":{"description":"GROUPE_INVALIDE — noms, bornes de capacité (20 groupes, 16 features)"},"409":{"description":"NOM_PRIS"}},"tags":["donnees"]}},"/v1/projects/{pid}/features/groupes/{nom}":{"delete":{"operationId":"supprimer_groupe_features","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Groupe supprimé (descripteur + entités versionnées)"},"404":{"description":"Groupe inconnu"}},"tags":["donnees"]}},"/v1/projects/{pid}/features/groupes/{nom}/ingester":{"post":{"operationId":"ingerer_version_features","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngesterVersionReq"}}},"required":true},"responses":{"200":{"description":"Version ingérée (numéro croissant par entité) — l'ingestion doit suivre EXACTEMENT le schéma du groupe"},"400":{"description":"INGESTION_REFUSEE — feature manquante/inconnue, tailles hors bornes, capacité atteinte"},"404":{"description":"Groupe inconnu"}},"tags":["donnees"]}},"/v1/projects/{pid}/features/transformations/valider":{"post":{"operationId":"valider_transformation_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValiderTransformationReq"}}},"required":true},"responses":{"200":{"description":"Transformation validée et appliquée aux échantillons"}},"tags":["features"]}},"/v1/projects/{pid}/features/{fid}/lineage":{"get":{"operationId":"lineage_feature_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom de la feature","in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lineage BFS de la feature : vague 0 la feature, vague 1 (amont) chaque dataset où une de ses versions est matérialisée, vague 2 (aval) TOUS les entraînements consommant ces datasets — dédupliqué, trié. Une feature inconnue rend VIDE, aucun chemin inventé ; les liens se posent EXPLICITEMENT, jamais déduits"}},"tags":["donnees"]}},"/v1/projects/{pid}/features/{groupe}":{"delete":{"operationId":"supprimer_feature_groupe","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"groupe","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Groupe et TOUTES ses versions supprimés"}},"tags":["general"]},"get":{"operationId":"detail_feature_groupe","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"groupe","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Groupe + entités versionnées"}},"tags":["general"]}},"/v1/projects/{pid}/features/{groupe}/ingestion":{"post":{"operationId":"ingestion_features","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"groupe","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngestionReq"}}},"required":true},"responses":{"201":{"description":"Version ingérée — le schéma du groupe doit être suivi exactement"}},"tags":["general"]}},"/v1/projects/{pid}/features/{groupe}/{entite}":{"get":{"operationId":"lire_entite_features","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"groupe","required":true,"schema":{"type":"string"}},{"in":"path","name":"entite","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lecture EN LIGNE : la dernière version de l'entité"}},"tags":["general"]}},"/v1/projects/{pid}/features/{groupe}/{entite}/historique":{"get":{"operationId":"historique_entite_features","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"groupe","required":true,"schema":{"type":"string"}},{"in":"path","name":"entite","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Versions conservées, la plus récente en dernier"}},"tags":["general"]}},"/v1/projects/{pid}/federation/callback":{"post":{"operationId":"callback_federation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallbackFederationReq"}}},"required":true},"responses":{"200":{"description":"Callback accepté : state vérifié, expiration mesurée, code posé — phase attente-échange"},"400":{"description":"Erreurs NOMMÉES : mauvaise_phase, demande_expiree, erreur_fournisseur, state_invalide, code_absent"},"404":{"description":"Aucun flux en cours"}},"tags":["securite"]}},"/v1/projects/{pid}/federation/claims":{"get":{"operationId":"lire_regles_claims","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles de mapping claims → rôles (évaluées par priorité croissante, la première correspondante tranche)"}},"tags":["securite"]},"put":{"operationId":"poser_regles_claims","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RegleClaimSerde"},"type":"array"}}},"required":true},"responses":{"200":{"description":"Règles posées (remplacent les précédentes) — claim non vide, rôle connu, priorités UNIQUES"},"400":{"description":"REGLES_INVALIDES — ambiguïté de priorité ou rôle inconnu, refus nommé"}},"tags":["securite"]}},"/v1/projects/{pid}/federation/claims/mapper":{"post":{"operationId":"mapper_claims_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MapperClaimsReq"}}},"required":true},"responses":{"200":{"description":"Rôle décidé par la première règle correspondante (priorité croissante)"},"404":{"description":"Aucune règle posée"},"422":{"description":"AUCUNE_REGLE_CORRESPOND — refus explicite, jamais de rôle deviné"}},"tags":["securite"]}},"/v1/projects/{pid}/federation/config":{"get":{"operationId":"lire_config_federation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Configuration courante (null si jamais posée)"},"404":{"description":"Aucune configuration posée"}},"tags":["securite"]},"put":{"operationId":"poser_config_federation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigFederationReq"}}},"required":true},"responses":{"200":{"description":"Fédération OIDC configurée : discovery PARSÉ (champs requis, https exigé — http sur localhost uniquement), client et redirection enregistrés"},"400":{"description":"CONFIG_REFUSEE — champ absent/vide, schéma non sécurisé, redirect non https"}},"tags":["securite"]}},"/v1/projects/{pid}/federation/demander":{"post":{"operationId":"demander_federation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Demande d'autorisation ouverte : state/nonce aléatoires, expiration MESURÉE (600 s) — l'URL d'autorisation complète est rendue"},"400":{"description":"DEMANDE_REFUSEE — client_id/redirect_uri invalides"},"404":{"description":"Aucune configuration posée"},"409":{"description":"FLUX_DEJA_EN_COURS — une seule demande à la fois"}},"tags":["securite"]}},"/v1/projects/{pid}/federation/echanger":{"post":{"operationId":"echanger_federation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EchangerFederationReq"}}},"required":true},"responses":{"200":{"description":"Session ouverte — claims MESURÉS (sub, email, iat, exp) et SIGNATURE vérifiée pour de vrai contre la JWKS de l'émetteur (RS256/ES256, kid, fetch réel du jwks_uri) ; JWKS injoignable → signature_verifiee: false AVEC la raison, jamais contournée"},"400":{"description":"Erreurs NOMMÉES du noyau (reponse_invalide, id_token_malforme, iss_inattendu, aud_inattendu, nonce_invalide, jeton_expire, algorithme_refuse, signature_invalide, kid_absent…)"},"404":{"description":"Aucun flux en attente d'échange"}},"tags":["securite"]}},"/v1/projects/{pid}/federation/flux":{"delete":{"operationId":"annuler_flux_federation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Flux en cours supprimé (n'importe quelle phase : demande, attente-échange, session) — audité federation.flux_annule ; la SESSION éventuelle vit sa vie (exp mesure seule)"},"404":{"description":"Aucun flux vivant — honnête, rien n'est inventé (idempotence : le 2e appel tombe ici)"}},"tags":["securite"]}},"/v1/projects/{pid}/federation/jwks":{"get":{"operationId":"lire_jwks_federation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"JWKS de l'émetteur : FETCH RÉEL depuis le jwks_uri de la discovery (cache par org — hit/miss MESURÉ, kids listés, conventions Keycloak constatées sans être imposées)"},"404":{"description":"Aucune configuration posée"},"502":{"description":"JWKS_INJOIGNABLE — fetch impossible, raison honnête, aucun contournement"}},"tags":["securite"]}},"/v1/projects/{pid}/federation/regles-defaut":{"get":{"operationId":"regles_defaut_federation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"PROPOSITION de règles de mapping (jamais posée automatiquement) : jeu Keycloak si l'issuer contient « /realms/ » (realm_access.roles, tableau JSON), proposition générique sinon (groups + plancher lecteur sur sub)"}},"tags":["securite"]}},"/v1/projects/{pid}/federation/session":{"get":{"operationId":"lire_session_federation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Session VIVANTE (sujet, courriel, issuer, émission, expiration MESURÉE sur le claim exp, signature_verifiee) — ou null honnête : aucune session, ou expirée (dit avec sa date mesurée)"}},"tags":["securite"]}},"/v1/projects/{pid}/ferroq/files":{"get":{"operationId":"console_ferroq_files","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"vhost de la file à décrire (exigé avec file)","in":"query","name":"vhost","required":false,"schema":{"type":"string"}},{"description":"nom de la file à DÉCRIRE (sans ce paramètre : liste de toutes les files du broker)","in":"query","name":"file","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Files du broker réel (LigneFile ferroq) ou description (cfg + stats mesurées)"}},"tags":["general"]}},"/v1/projects/{pid}/ferroq/files/{nom}/acquitter":{"post":{"operationId":"console_ferroq_acquitter","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom de la file","in":"path","name":"nom","required":true,"schema":{"type":"string"}},{"description":"vhost de la file (exigé)","in":"query","name":"vhost","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FerroqAcquitterReq"}}},"required":true},"responses":{"200":{"description":"Bilan compté PAR LE BROKER (acquettes/rejettees/prolongees/inconnues)"}},"tags":["general"]}},"/v1/projects/{pid}/ferroq/files/{nom}/dlq":{"get":{"operationId":"console_ferroq_dlq","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom de la file","in":"path","name":"nom","required":true,"schema":{"type":"string"}},{"description":"vhost de la file (exigé)","in":"query","name":"vhost","required":true,"schema":{"type":"string"}},{"description":"filtre d'état de la console : en_attente | echoue | livre | tous (défaut : tous)","in":"query","name":"etat","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lettres mortes du broker réel (motif rapporté) — vue console filtrable par état"}},"tags":["general"]}},"/v1/projects/{pid}/ferroq/files/{nom}/dlq/purger":{"post":{"operationId":"console_ferroq_dlq_purger","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom de la file","in":"path","name":"nom","required":true,"schema":{"type":"string"}},{"description":"vhost de la file (exigé)","in":"query","name":"vhost","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Nombre de lettres mortes purgées — MESURÉ par le broker"}},"tags":["general"]}},"/v1/projects/{pid}/ferroq/files/{nom}/dlq/redriver":{"post":{"operationId":"console_ferroq_dlq_redriver","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom de la file","in":"path","name":"nom","required":true,"schema":{"type":"string"}},{"description":"vhost de la file (exigé)","in":"query","name":"vhost","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FerroqRedriverReq"}}},"required":true},"responses":{"200":{"description":"Rejeu : nombre de lettres mortes remises en file — MESURÉ par le broker"}},"tags":["general"]}},"/v1/projects/{pid}/ferroq/files/{nom}/purger":{"post":{"operationId":"console_ferroq_purger","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom de la file","in":"path","name":"nom","required":true,"schema":{"type":"string"}},{"description":"vhost de la file (exigé)","in":"query","name":"vhost","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Nombre de messages purgés — MESURÉ par le broker"}},"tags":["general"]}},"/v1/projects/{pid}/ferroq/files/{nom}/tirer":{"post":{"operationId":"console_ferroq_tirer","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom de la file","in":"path","name":"nom","required":true,"schema":{"type":"string"}},{"description":"vhost de la file (exigé)","in":"query","name":"vhost","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FerroqTirerReq"}}},"required":true},"responses":{"200":{"description":"Messages tirés du broker réel (LivraisonRep ferroq, charges décodées)"}},"tags":["general"]}},"/v1/projects/{pid}/ferroq/url":{"delete":{"operationId":"supprimer_ferroq_url","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Configuration retirée — la console refusera honnêtement"}},"tags":["general"]},"get":{"operationId":"lire_ferroq_url","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"URL du broker configuré (null si aucune)"}},"tags":["general"]},"put":{"operationId":"poser_ferroq_url","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FerroqUrlReq"}}},"required":true},"responses":{"201":{"description":"Broker configuré pour la console des files de cette organisation"}},"tags":["general"]}},"/v1/projects/{pid}/firewall":{"get":{"operationId":"lister_regles_fw","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["reseau"]},"post":{"operationId":"creer_regle_fw","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRegleFwReq"}}},"required":true},"responses":{"201":{"description":"Créé"}},"tags":["reseau"]}},"/v1/projects/{pid}/firewall/bans":{"get":{"operationId":"lister_bans","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"IPs bannies (auto + manuel)"}},"tags":["reseau"]},"post":{"operationId":"bannir_manuel","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewBanReq"}}},"required":true},"responses":{"201":{"description":"IP bannie manuellement"}},"tags":["reseau"]}},"/v1/projects/{pid}/firewall/bans/{ip}":{"delete":{"operationId":"debannir_ip","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"ip","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"IP débannie"}},"tags":["reseau"]}},"/v1/projects/{pid}/firewall/intelligence":{"get":{"operationId":"intelligence_pf","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Scores, top IP, stats, verdicts"}},"tags":["reseau"]}},"/v1/projects/{pid}/firewall/l7":{"get":{"operationId":"lister_regles_l7","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles L7 actives"}},"tags":["reseau"]},"post":{"operationId":"creer_regle_l7","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRegleL7Req"}}},"required":true},"responses":{"201":{"description":"Règle L7 créée"}},"tags":["reseau"]}},"/v1/projects/{pid}/firewall/l7/{rid}":{"delete":{"operationId":"supprimer_regle_l7","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règle supprimée"}},"tags":["reseau"]}},"/v1/projects/{pid}/firewall/limitation":{"get":{"operationId":"limiter_limitation_waf","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles posées + compteurs mesurés de la fenêtre courante"}},"tags":["securite"]},"put":{"operationId":"poser_limitation_waf","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LimitationWafReq"}}},"required":true},"responses":{"200":{"description":"Règles posées — compteurs remis à zéro, le middleware les applique à partir de la requête suivante"},"400":{"description":"Une règle est invalide (nommée)"}},"tags":["securite"]}},"/v1/projects/{pid}/firewall/limitation/{nom}":{"delete":{"operationId":"supprimer_limitation_waf","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règle retirée (compteur avec)"},"404":{"description":"Règle inconnue"}},"tags":["securite"]}},"/v1/projects/{pid}/firewall/settings":{"get":{"operationId":"mode_fw","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"Mode du pare-feu projet : enforce (bloque) ou audit (journalise sans\nbloquer — pour valider les règles avant enforcement).","tags":["reseau"]},"put":{"operationId":"definir_mode_fw","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"OK"}},"tags":["reseau"]}},"/v1/projects/{pid}/firewall/suggestions":{"get":{"operationId":"suggerer_regles_waf","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles PROPOSÉES par le mode learning (journal réel des bans) — jamais posées automatiquement"}},"tags":["securite"]}},"/v1/projects/{pid}/firewall/suggestions/accepter":{"post":{"operationId":"accepter_suggestion_waf","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccepterSuggestionReq"}}},"required":true},"responses":{"200":{"description":"Suggestion matérialisée en RegleLimitation, posée au registre (compteurs repartis à zéro)"},"404":{"description":"Suggestion inconnue du journal courant"}},"tags":["securite"]}},"/v1/projects/{pid}/firewall/test":{"post":{"operationId":"tester_regle_pf","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequeteEval"}}},"required":true},"responses":{"200":{"description":"Verdict prédit SANS exécuter la requête"}},"tags":["reseau"]}},"/v1/projects/{pid}/firewall/{fid}":{"delete":{"operationId":"supprimer_regle_fw","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["reseau"]}},"/v1/projects/{pid}/firewall/{fid}/counters":{"get":{"operationId":"compteurs_regle","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"Compteurs d'une règle : paquets, octets, drops (simulation déterministe\ncôté contrôle ; un vrai driver lirait les compteurs nftables).","tags":["reseau"]}},"/v1/projects/{pid}/flux":{"get":{"operationId":"lister_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Flux de streaming de l'org"}},"tags":["general"]},"post":{"operationId":"creer_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewFluxReq"}}},"required":true},"responses":{"201":{"description":"Flux créé"}},"tags":["general"]}},"/v1/projects/{pid}/flux/{fid}":{"delete":{"operationId":"supprimer_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Flux supprimé (enregistrements perdus)"}},"tags":["general"]}},"/v1/projects/{pid}/flux/{fid}/enregistrer":{"post":{"operationId":"ecrire_dans_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnregistrerReq"}}},"required":true},"responses":{"201":{"description":"Enregistrement écrit, séquence + partition"}},"tags":["general"]}},"/v1/projects/{pid}/flux/{fid}/lire":{"post":{"operationId":"lire_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LireReq"}}},"required":true},"responses":{"200":{"description":"Enregistrements après le curseur + nouveau curseur"}},"tags":["general"]}},"/v1/projects/{pid}/flux/{fid}/pont":{"get":{"operationId":"lire_pont_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pont du flux"},"404":{"description":"Aucun pont posé"}},"tags":["general"]},"put":{"operationId":"poser_pont_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PontFluxReq"}}},"required":true},"responses":{"200":{"description":"Pont créé (curseur 0 — rien encore consommé)"}},"tags":["general"]}},"/v1/projects/{pid}/flux/{fid}/pont/consommer":{"post":{"operationId":"consommer_pont_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsommerPontReq"}}},"required":true},"responses":{"200":{"description":"Lot prélevé : charge utile insertAll PRÊTE À POSTER (l'insertion réelle côté entrepôt reste à l'intégration), curseur avancé"}},"tags":["general"]}},"/v1/projects/{pid}/flux/{fid}/pont/etat":{"put":{"operationId":"changer_etat_pont_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EtatPontReq"}}},"required":true},"responses":{"200":{"description":"Pont suspendu/repris"}},"tags":["general"]}},"/v1/projects/{pid}/flux/{fid}/rejouer":{"post":{"operationId":"rejouer_fenetre_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejouerFenetreReq"}}},"required":true},"responses":{"200":{"description":"Événements de la fenêtre, DÉDUPLIQUÉS par séquence (une réinjection ne rejoue pas deux fois le même)"}},"tags":["general"]}},"/v1/projects/{pid}/flux/{fid}/shards":{"get":{"operationId":"lire_shards_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Affectations courantes + charge MESURÉE de chaque nœud (comptée sur les enregistrements RÉELS retenus par chaque shard)"},"404":{"description":"Flux inconnu ou sans flotte de shards"}},"tags":["general"]},"put":{"operationId":"poser_shards_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoeudsShardsReq"}}},"required":true},"responses":{"200":{"description":"Affectations posées par tourniquet (écart ≤ 1 VÉRIFIÉ, pas supposé)"},"400":{"description":"SHARDS_INVALIDES — bornes et doublons nommés"},"404":{"description":"Flux inconnu"}},"tags":["general"]}},"/v1/projects/{pid}/flux/{fid}/shards/{partition}":{"put":{"operationId":"basculer_shard_flux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}},{"in":"path","name":"partition","required":true,"schema":{"minimum":0,"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BasculeShardReq"}}},"required":true},"responses":{"200":{"description":"Shard basculé — l'ANCIEN nœud est rendu"},"400":{"description":"BASCULE_REFUSEE — shard inconnu, nœud inconnu, shard déjà en place"},"404":{"description":"Flux inconnu ou sans flotte de shards"}},"tags":["general"]}},"/v1/projects/{pid}/fonctions":{"get":{"operationId":"lister_fonctions","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Fonctions serverless de l'organisation"}},"tags":["apps"]},"post":{"operationId":"creer_fonction","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewFonctionReq"}}},"required":true},"responses":{"201":{"description":"Fonction déployée en pod (cluster plateforme) — URL d'appel réelle rendue"}},"tags":["apps"]}},"/v1/projects/{pid}/fonctions/declencheurs":{"get":{"operationId":"lister_declencheurs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Déclencheurs déclarés (genre + cible + fonction liée) — un descripteur INVALIDE ne déclenche jamais rien (revalidé à chaque événement)"}},"tags":["compute"]},"post":{"operationId":"creer_declencheur_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeclencheurApiReq"}}},"required":true},"responses":{"201":{"description":"Déclencheur validé à la pose (chaque genre impose sa forme de cible — http chemin absolu, regle rgl-…, topic top-… + préfixe, cron 5 champs)"},"400":{"description":"DECLENCHEUR_INVALIDE — genre inconnu ou cible en forme fausse, raisons nommées"},"404":{"description":"Fonction liée inconnue de l'org"}},"tags":["compute"]}},"/v1/projects/{pid}/fonctions/declencheurs/test":{"post":{"operationId":"tester_declencheurs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvenementEntrantReq"}}},"required":true},"responses":{"200":{"description":"Test à blanc : fonctions qui s'enflamment pour cet événement (dédupliquées, ordre de déclaration) — un déclencheur invalide ne déclenche RIEN"}},"tags":["compute"]}},"/v1/projects/{pid}/fonctions/declencheurs/{did}":{"delete":{"operationId":"supprimer_declencheur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"dcl-…","in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Déclencheur retiré"},"404":{"description":"Déclencheur inconnu"}},"tags":["compute"]}},"/v1/projects/{pid}/fonctions/knative/etat":{"get":{"operationId":"etat_knative_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"État RÉEL des pods knative-serving/knative-eventing lus du cluster — « jamais déployé » rendu comme tel ; en harnais (CIRRO_WORKLOADS_REELS=0) l'état cluster n'est pas consulté, jamais simulé"},"503":{"description":"CLUSTER_INJOIGNABLE — le canal kubectl ne répond pas, aucun état inventé"}},"tags":["fonctions"]}},"/v1/projects/{pid}/fonctions/knative/offre":{"get":{"operationId":"offre_knative","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Domaine par défaut des routes ksvc (ex. fonctions.cirro) — rend le plan d'installation EXACT, jamais exécuté ici","in":"query","name":"domaine","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Offre honnête : version épinglée, licences (Apache-2.0), fonctions mesurables, dépendances système CITÉES + plan d'installation exact si ?domaine="},"400":{"description":"DOMAINE_INVALIDE / PLAN_INSANE — le plan généré ne passe pas sa propre garde (URL non épinglée, latest, domaine absent)"}},"tags":["fonctions"]}},"/v1/projects/{pid}/fonctions/pools-warm":{"get":{"operationId":"lister_pools_warm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pools de warm-starts par runtime : configuration, instances chaudes, compteurs MESURÉS (réutilisations/froids) et froid évité cumulé"}},"tags":["compute"]}},"/v1/projects/{pid}/fonctions/pools-warm/{runtime}":{"put":{"operationId":"poser_pool_warm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"python | nodejs — un pool par runtime","in":"path","name":"runtime","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigPoolReq"}}},"required":true},"responses":{"200":{"description":"Pool créé : compteurs à zéro — jamais rien déduit d'un passé qui n'existe pas encore"},"400":{"description":"POOL_INVALIDE — runtime inconnu, bornes ou durée de froid hors clous, raisons nommées"}},"tags":["compute"]}},"/v1/projects/{pid}/fonctions/pools-warm/{runtime}/demarrages":{"post":{"operationId":"consigner_demarrage_warm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"runtime","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemarrageWarmReq"}}},"required":true},"responses":{"200":{"description":"Instance chaude consignée dans le pool (id creus/double/pool plein refusés nommément)"},"400":{"description":"DEMARRAGE_REFUSE — id invalide, doublon ou pool plein (nettoyez avant)"},"404":{"description":"Pool inconnu"}},"tags":["compute"]}},"/v1/projects/{pid}/fonctions/pools-warm/{runtime}/invoquer":{"post":{"operationId":"invoquer_pool_warm_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"runtime","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoquerWarmReq"}}},"required":true},"responses":{"200":{"description":"Décision MESURÉE pour CETTE invocation : réutiliser (l'instance la plus récente, âge et froid évité cités) ou démarrer à froid (raison)"},"404":{"description":"Pool inconnu — posez sa configuration d'abord"}},"tags":["compute"]}},"/v1/projects/{pid}/fonctions/pools-warm/{runtime}/nettoyer":{"post":{"operationId":"nettoyer_pool_warm_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"runtime","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoquerWarmReq"}}},"required":true},"responses":{"200":{"description":"Ramasse-miettes mesuré : instances oisives ≥ inactivite_max_s éteintes (les plus âgées d'abord), SANS descendre sous instances_min — ids éteints rendus"},"404":{"description":"Pool inconnu"}},"tags":["compute"]}},"/v1/projects/{pid}/fonctions/{fid}":{"delete":{"operationId":"supprimer_fonction","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Fonction supprimée (pod, service, configmap)"}},"tags":["apps"]}},"/v1/projects/{pid}/fonctions/{fid}/knative":{"get":{"operationId":"manifestes_knative_fonction","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Identifiant de la fonction (fn-…)","in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Manifestes Knative de la fonction : ConfigMap du code + Service (ksvc, limites portées, scale-to-zero) + PingSource par déclencheur cron ; les déclencheurs topic/règle sont dits comme des PONTS existants, pas des Triggers inventés"},"404":{"description":"Fonction inconnue dans l'org"}},"tags":["fonctions"]}},"/v1/projects/{pid}/fonctions/{fid}/limites":{"get":{"operationId":"lire_limites_fonction","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Bornes durée/mémoire (défauts façon Lambda : 30 s / 512 Mo)"}},"tags":["calcul"]},"put":{"operationId":"poser_limites_fonction","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoserLimitesFonctionReq"}}},"required":true},"responses":{"200":{"description":"Bornes posées (1 s-5 min, 128 Mo-4 Go)"},"400":{"description":"Bornes hors plage"}},"tags":["calcul"]}},"/v1/projects/{pid}/fonctions/{fid}/limites/verdict":{"post":{"operationId":"verdict_limites_fonction","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerdictLimitesReq"}}},"required":true},"responses":{"200":{"description":"Verdict PUR sur des mesures fournies — chaque cause cite son dépassement exact"}},"tags":["calcul"]}},"/v1/projects/{pid}/fonctions/{fid}/metriques":{"get":{"operationId":"lire_metriques_fonction","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Agrégats MESURÉS sur le journal des appels (invocations 0 = zéros honnêtes, aucune latence déduite d'un silence)"}},"tags":["calcul"]}},"/v1/projects/{pid}/fonctions/{fid}/runtime-conteneur":{"put":{"operationId":"poser_runtime_conteneur_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"fn-…","in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuntimeConteneurReq"}}},"required":true},"responses":{"200":{"description":"Descripteur posé : image étiquetée (règle cirroAmplify, une seule source de vérité) + handler propre (il finit dans une ligne de commande)"},"400":{"description":"RUNTIME_INVALIDE — image flottante ou handler impropre, raisons nommées"},"404":{"description":"Fonction inconnue de l'org"}},"tags":["compute"]}},"/v1/projects/{pid}/fonctions/{fid}/runtime-conteneur/agregats":{"post":{"operationId":"agreger_runtime_conteneur_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/InvocationConteneurApiReq"},"type":"array"}}},"required":true},"responses":{"200":{"description":"Agrégats MESURÉS des invocations apportées par l'exécutant réel : p50/p90 nearest-rank, froids comptés, pic mémoire observé — sans invocation, tout à zéro honnêtement"},"404":{"description":"Fonction sans runtime conteneurisé déclaré"}},"tags":["compute"]}},"/v1/projects/{pid}/fonctions/{fid}/tester":{"post":{"operationId":"tester_fonction","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Appel HTTP de la fonction — réponse JSON"}},"tags":["apps"]}},"/v1/projects/{pid}/garde":{"get":{"operationId":"lister_constats_garde","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Constats de corrélation ouverts et fermés"}},"tags":["securite"]}},"/v1/projects/{pid}/garde/analyser":{"post":{"operationId":"analyser_garde","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Analyse exécutée : anomalies ouvertes corrélées, nouveaux constats (dédupliqués)"}},"tags":["securite"]}},"/v1/projects/{pid}/garde/regles":{"get":{"operationId":"lister_regles_custom_garde","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles custom de l'org"}},"tags":["securite"]},"post":{"operationId":"creer_regle_custom_garde","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegleCustomReq"}}},"required":true},"responses":{"201":{"description":"Règle créée (validée par le parseur du module)"},"400":{"description":"Expression impossible à parser, sévérité hors bornes…"}},"tags":["securite"]}},"/v1/projects/{pid}/garde/regles/evaluer":{"post":{"operationId":"evaluer_regles_custom_garde","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Déclenchements matérialisés en constats (dédupliqués)"}},"tags":["securite"]}},"/v1/projects/{pid}/garde/regles/{rid}":{"delete":{"operationId":"supprimer_regle_custom_garde","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règle supprimée"},"404":{"description":"Règle inconnue"}},"tags":["securite"]}},"/v1/projects/{pid}/garde/sources":{"get":{"operationId":"bilan_sources_garde","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Fenêtre d'agrégation (jours)","in":"query","name":"fenetre_jours","required":true,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"description":"Bilan : agrégats (source × jour), acteurs suspects nommés, dates illisibles et hors fenêtre COMPTÉS"}},"tags":["securite"]},"post":{"operationId":"ingerer_sources_garde","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngererSourcesReq"}}},"required":true},"responses":{"200":{"description":"Événements bruts ingérés (validés un à un — un lot sale est refusé entier)"}},"tags":["securite"]}},"/v1/projects/{pid}/garde/{fid}":{"delete":{"operationId":"fermer_constat_garde","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Constat fermé (conservé pour l'historique)"}},"tags":["securite"]}},"/v1/projects/{pid}/git/liaison-sso":{"get":{"operationId":"lire_liaison_sso_git","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Liaison SSO courante + endpoints OAuth2 de la forge"},"404":{"description":"Aucune liaison posée"}},"tags":["depots"]},"put":{"operationId":"poser_liaison_sso_git","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiaisonSsoGitea"}}},"required":true},"responses":{"200":{"description":"Liaison SSO posée — le secret client n'est qu'une RÉFÉRENCE cirroSecrets, un jeton collé est refusé"}},"tags":["depots"]}},"/v1/projects/{pid}/git/miroir/verifier":{"post":{"operationId":"verifier_miroir_git","parameters":[{"description":" Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifierMiroirReq"}}},"required":true},"responses":{"200":{"description":"État du miroir DÉRIVÉ des seules empreintes mesurées : fidele | divergent | source_injoignable | jamais_verifie | empreinte_invalide — une vérification FIDÈLE met à jour l'horodatage de dernière sauvegarde"},"404":{"description":"Aucun plan posé"}},"tags":["depots"]}},"/v1/projects/{pid}/git/offre":{"get":{"operationId":"offre_gitea","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom DNS de la forge (ex. git.nantes.cirro) — rend le plan de déploiement EXACT","in":"query","name":"hote","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Offre honnête : version épinglée, fonctions, dépendances système CITÉES + plan docker EXACT (jamais exécuté ici)"},"400":{"description":"HOTE_INVALIDE — vide ou > 100 caractères"}},"tags":["depots"]}},"/v1/projects/{pid}/git/plan-sauvegarde":{"get":{"operationId":"lire_plan_sauvegarde_git","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plan de sauvegarde + commandes EXACTES + échéance MESURÉE (due/jamais faite)"},"404":{"description":"Aucun plan posé"}},"tags":["depots"]},"put":{"operationId":"poser_plan_sauvegarde_git","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanSauvegardeGitea"}}},"required":true},"responses":{"200":{"description":"Plan de sauvegarde posé (rsync|borg, commandes exactes rendues jamais exécutées)"}},"tags":["depots"]}},"/v1/projects/{pid}/glacier/plan-cycle-vie":{"get":{"operationId":"plan_cycle_vie_bucket","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"NOM du bucket à planifier","in":"query","name":"bucket","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plan MESURÉ sur le listage réel du bucket (sélection, octets, âges inconnus cités — jamais archivés)"},"503":{"description":"Passerelle S3 inactive : aucun objet inventé"}},"tags":["donnees"]}},"/v1/projects/{pid}/glacier/regles-cycle-vie":{"get":{"operationId":"lister_regles_cycle_vie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles de cycle de vie posées (préfixe, âge en jours mesuré sur la dernière modification)"}},"tags":["donnees"]},"put":{"operationId":"poser_regles_cycle_vie","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RegleCycleVieReq"},"type":"array"}}},"required":true},"responses":{"200":{"description":"Règles remplacées (première règle qui attrape une clé gagne, façon lifecycle S3)"},"400":{"description":"Règle invalide ou id dupliqué"}},"tags":["donnees"]}},"/v1/projects/{pid}/glacier/{aid}/fuse":{"post":{"operationId":"plan_fuse_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"arc-…","in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanFuseReq"}}},"required":true},"responses":{"200":{"description":"Plan de montage + rapport MESURÉ de la simulation : chaque lecture est bornée (fin d'archive, plafond par lecture, plafond global), les octets épargnés face à une restauration complète sont chiffrés"},"400":{"description":"FUSE_REFUSE — point de montage non absolu/traversant, plafond nul, archive purgée, lecture refusée (offset hors archive, plafond épuisé)"},"404":{"description":"Archive inconnue"}},"tags":["stockage"]}},"/v1/projects/{pid}/glacier/{aid}/replication":{"post":{"operationId":"plan_replication_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanReplicationReq"}}},"required":true},"responses":{"200":{"description":"Plan de réplication : l'empreinte cible MESURÉE tranche l'état (absente_cote_cible / identiques / divergentes) — identiques ne transfère RIEN (dit tel), sinon commandes exactes rendues, JAMAIS exécutées"},"400":{"description":"REPLICATION_REFUSEE — racine non absolue, org manquant, empreinte cible vide, archive purgée"},"404":{"description":"Archive inconnue"}},"tags":["stockage"]}},"/v1/projects/{pid}/graphql":{"post":{"operationId":"executer_graphql","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GraphqlReq"}}},"required":true},"responses":{"200":{"description":"Query exécutée sur les ressources réelles — format GraphQL {data}/{errors}"}},"tags":["general"]}},"/v1/projects/{pid}/graphql/abonnements":{"get":{"operationId":"lister_abonnements_graphql","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Abonnements SSE déclarés (filtre par préfixe de genre)"}},"tags":["systeme"]},"post":{"operationId":"creer_abonnement_graphql","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbonnementSseReq"}}},"required":true},"responses":{"201":{"description":"Abonnement créé — le flux SSE se lit par GET /graphql/abonnements/{id}/sse"},"400":{"description":"ABONNEMENT_INVALIDE — id et préfixe de genre nommés"},"409":{"description":"DEJA_PRIS — un abonnement porte déjà cet id"}},"tags":["systeme"]}},"/v1/projects/{pid}/graphql/abonnements/{id}/sse":{"get":{"operationId":"flux_sse_abonnement_graphql","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Flux SSE de l'abonnement : événements du bus FILTRÉS par préfixe de genre, chaque événement sérialisé par le noyau SSE (id = position MESURÉE dans CE flux)"},"404":{"description":"Abonnement inconnu"}},"tags":["systeme"]}},"/v1/projects/{pid}/graphql/complexite":{"post":{"operationId":"complexite_graphql_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComplexiteReq"}}},"required":true},"responses":{"200":{"description":"Verdict de complexité HEURISTIQUE et BORNÉ : +1 par champ demandé, +3 par niveau (racine comprise), +2 par alias et par argument — le compte chiffré, la limite (200) et le verdict (admise, ou refus NOMMÉ et chiffré au-delà) ; on compte ce qui est DEMANDÉ, pas ce que le résolveur fera"},"400":{"description":"REQUETE_INVALIDE — requête non parsable (position de l'erreur citée), trop grande ou trop profonde (bornes du parseur)"}},"tags":["general"]}},"/v1/projects/{pid}/graphql/mutation":{"post":{"operationId":"executer_mutation_graphql","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MutationGraphqlReq"}}},"required":true},"responses":{"200":{"description":"Mutation AUTORISÉE puis EXÉCUTÉE (l'action réelle suit : VM, app, secret, budget) — chaque refus nomme la mutation, la permission et l'argument"},"400":{"description":"MUTATION_INVALIDE — parse GraphQL (une mutation par document, arguments littéraux)"},"403":{"description":"MUTATION_REFUSEE — permission manquante pour CE rôle"}},"tags":["systeme"]}},"/v1/projects/{pid}/graphql/plugins":{"get":{"operationId":"contrat_plugins_graphql","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Le CONTRAT des plugins de vues métier : étendre le graphe SANS toucher au cœur — un champ racine de plus, ses champs, leur source ; types FERMÉS (String, Int, Float, Boolean, JSON), nom slug 2-40 sans collision avec le schéma servi (noms réservés DÉRIVÉS de schema(), ils ne peuvent pas diverger), sources non vides, doublons internes refusés ; aucun registre de plugins n'est encore persisté — dit tel quel, la validation dry-run : POST …/graphql/plugins/valider"}},"tags":["general"]}},"/v1/projects/{pid}/graphql/plugins/valider":{"post":{"operationId":"valider_plugin_graphql","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PluginVueReq"}}},"required":true},"responses":{"200":{"description":"Dry-run de validation : verdict VALIDE (plugin rendu avec ses champs typés et leurs sources) — DRY-RUN SEULEMENT, rien n'est persisté, le graphe servi ne bouge pas"},"400":{"description":"PLUGIN_INVALIDE — refus NOMMÉ par garde franchie : nom mal formé, collision avec le schéma servi, zéro champ, champ/type/source inconnus (cités), champ dupliqué"}},"tags":["general"]}},"/v1/projects/{pid}/graphql/schema":{"get":{"operationId":"schema_graphql","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Le sous-ensemble GraphQL réellement implémenté — types, champs, arguments"}},"tags":["general"]}},"/v1/projects/{pid}/iam/audit/export":{"get":{"operationId":"export_audit_membre_iam","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Acteur exact du journal d'audit (filtre EXACT, pas une sous-chaîne)","in":"query","name":"membre","required":true,"schema":{"type":"string"}},{"description":"csv (RFC 4180, text/csv + Content-Disposition) | json (défaut)","in":"query","name":"format","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Export du journal d'audit de l'org FILTRÉ par acteur exact, trié par horodatage croissant — entrées RÉELLES de la chaîne d'audit attribuables à cette org ; CSV : métadonnées préfixées # + en-tête acteur,action,cible,ts ; JSON : document membre/nombre/genere_le/entrees. Admin d'org ou plateforme (même discipline que /v1/audit)"},"400":{"description":"MEMBRE_REQUIS / FORMAT_INVALIDE"},"403":{"description":"ADMIN_PLATEFORME_REQUIS — l'audit est réservé aux opérateurs de la plateforme et aux administrateurs d'organisation"}},"tags":["iam"]}},"/v1/projects/{pid}/iam/roles/custom":{"get":{"operationId":"roles_custom_iam","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Le CONTRAT des rôles personnalisés : services visés (liste FERMÉE), verbes admis et leur hiérarchie (admin > ecrire > lire — une permission couvre les verbes plus faibles), règles de validation (nom slug 2-40 sans collision avec les rôles fixes, au moins une permission, aucune dupliquée) ; aucun registre de rôles custom n'est encore persisté — dit tel quel, le dry-run de validation : POST …/roles/custom/valider"}},"tags":["iam"]}},"/v1/projects/{pid}/iam/roles/custom/valider":{"post":{"operationId":"valider_role_custom_iam","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleCustomReq"}}},"required":true},"responses":{"200":{"description":"Dry-run de validation : verdict VALIDE (avec la matrice complète de ce que le rôle autorise, service par service, verbe par verbe) ou REFUSÉ — DRY-RUN SEULEMENT, rien n'est persisté"},"400":{"description":"ROLE_INVALIDE — refus NOMMÉ par garde franchie : nom mal formé, collision avec un rôle fixe, zéro permission, service/verbe inconnu (cités), permission dupliquée"}},"tags":["iam"]}},"/v1/projects/{pid}/impact/{type}/{id}":{"get":{"operationId":"analyse_impact","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"type","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["systeme"]}},"/v1/projects/{pid}/inspections":{"get":{"operationId":"lister_inspections","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Historique des scans (échecs inclus)"}},"tags":["materiel"]}},"/v1/projects/{pid}/inspections/{sid}":{"post":{"operationId":"lancer_inspection","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Scan exécuté (ou échec SSH enregistré honnêtement)"}},"tags":["materiel"]}},"/v1/projects/{pid}/instances":{"get":{"operationId":"lister_instances","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstancesListe"}}},"description":"Instances réelles du projet (slug d'org en pid). SANS admin_password : le mot de passe n'est rendu qu'UNE SEULE FOIS, à la création (200, code réellement servi)"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Jeton absent/invalide"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"HORS_ORGANISATION / PERMISSION_REFUSEE — project.read requis"}},"tags":["donnees"]},"post":{"operationId":"creer_instance","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewInstanceReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstanceCree"}}},"description":"Instance provisionnée (nœuds démarrés). Placement k8s : PVC de données data-<nom>-<genre> (local-path, 8 Gi par défaut, taille réglable via k8s.taille_pvc_gb) monté sur le répertoire de données du genre. admin_password rendu UNE SEULE FOIS à la création (jamais ensuite en liste ni au détail, comme les workspaces) ; VIDE pour ferroquery : son serveur n'accepte ni --token ni env — le jeton se crée dans le pod (voir connexion)."},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"NOM_INVALIDE / NOEUDS_INVALIDES / GENRE_INVALIDE — validation de la demande"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Jeton absent/invalide"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"HORS_ORGANISATION / PERMISSION_REFUSEE — project.write requis"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"DEJA_EXISTANT — une instance porte déjà ce nom dans l'organisation"},"429":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"LIMITE_INSTANCES — garde MAX_INSTANCES_ORG atteinte"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Limite d'instances atteinte, binaire non configuré ou plage de ports saturée"}},"tags":["donnees"]}},"/v1/projects/{pid}/instances/pitr":{"post":{"operationId":"restaurer_pitr_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PitrReq"}}},"required":true},"responses":{"200":{"description":"Journal VALIDÉ puis rejoué dans l'ordre des LSN jusqu'à T : entrées appliquées/écartées comptées, bornes du rejeu rendues, écritures par genre, lignes avant/après"},"400":{"description":"PITR_REFUSE — instant illisible, genre inconnu, LSN répété, horodatages qui remontent le temps (journal incohérent, refusé)"}},"tags":["donnees"]}},"/v1/projects/{pid}/instances/{iid}":{"delete":{"operationId":"supprimer_instance","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"iid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Instance arrêtée et données supprimées (k8s : deploy, svc, PVC de données data-<nom>-<genre> ET netpol dédiée, par étiquette puis par nom"}},"tags":["donnees"]},"get":{"operationId":"detail_instance","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"iid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail (ports par nœud). SANS admin_password : rendu une seule fois à la création (201), comme les workspaces"}},"tags":["donnees"]}},"/v1/projects/{pid}/instances/{iid}/pgweb":{"delete":{"operationId":"supprimer_pgweb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Identifiant de l'instance","in":"path","name":"iid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"pgweb supprimé PAR ÉTIQUETTE cirrostratus.io/pgweb=<iid> (deploy, svc, netpol, secret) — rien d'orphelin ; clé meta pgweb:<iid> purgée. La suppression de l'INSTANCE emporte son pgweb avec elle"}},"tags":["donnees"]},"get":{"operationId":"etat_pgweb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Identifiant de l'instance","in":"path","name":"iid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"État RÉEL du pgweb : pods lus dans le cluster par le canal kubectl (comme le monitoring), url persistée en meta — deploye=false honnête si rien ne tourne ; en harnais (CIRRO_WORKLOADS_REELS=0) l'état cluster n'est pas consulté, jamais simulé"}},"tags":["donnees"]},"post":{"operationId":"deployer_pgweb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Identifiant de l'instance (ferrodb/postgres sur le cluster plateforme)","in":"path","name":"iid","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Pod pgweb déployé dans le namespace de l'org, auto-connecté à l'instance (URL de connexion en Secret Kubernetes référencé par secretKeyRef, jamais en env en clair) ; url = http://<ip-nœud>:<nodePort>, joignable depuis le LAN. 400 honnête si l'instance n'est ni ferrodb ni postgres ou n'est pas sur le cluster plateforme ; 409 si un pgweb existe déjà ; 501 si CIRRO_WORKLOADS_REELS=0 (aucun pod déployé depuis un harnais)"},"400":{"description":"Instance non explorable (genre ou placement) — la raison est donnée"}},"tags":["donnees"]}},"/v1/projects/{pid}/instances/{iid}/restaurer":{"post":{"operationId":"restaurer_instance","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"iid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"Restauré depuis la sauvegarde (dernière par défaut)"}},"tags":["donnees"]}},"/v1/projects/{pid}/instances/{iid}/snapshot":{"post":{"operationId":"snapshot_instance","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"iid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Sauvegarde créée (instance brièvement arrêtée puis relancée)"}},"tags":["donnees"]}},"/v1/projects/{pid}/instances/{iid}/topologie":{"get":{"operationId":"lire_topologie_instance","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"iid","required":true,"schema":{"type":"string"}},{"description":"Retard maximum exigé pour les lectures (ms, défaut 100)","in":"query","name":"retard_max_ms","required":false,"schema":{"format":"int64","minimum":0,"type":"integer"}}],"responses":{"200":{"description":"Topologie déclarée + routing dérivé : l'écriture ne va JAMAIS qu'au primaire (refusé s'il est hs), la lecture vers la réplique saine la moins retardée qui tient le retard — à défaut le primaire, dit tel"},"404":{"description":"Aucune topologie déclarée pour cette instance"}},"tags":["donnees"]},"put":{"operationId":"poser_topologie_instance","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"iid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopologieReq"}}},"required":true},"responses":{"200":{"description":"Topologie déclarée TELLE QUE MESURÉE par la sonde (rôles, retards ms, états sain/hs) — le routing dérivé est rendu : écriture → primaire, lecture → réplique la moins retardée qui tient le retard exigé"},"400":{"description":"TOPOLOGIE_INVALIDE — rôle erroné ou nœud répété, raisons nommées"}},"tags":["donnees"]}},"/v1/projects/{pid}/instances/{iid}/topologie/bascule-lecture":{"post":{"operationId":"basculer_lecture_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"iid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BasculeLectureReq"}}},"required":true},"responses":{"200":{"description":"Réplique retirée du pool (hs, motif enregistré) + nouvelle cible de lecture rendue — la bascule N'EST PAS une promotion, la perte de données à évaluer est une autre décision"},"400":{"description":"BASCULE_REFUSEE — motif vide, nœud inconnu ou PRIMAIRE visé (une bascule de lecture ne le retire pas)"},"404":{"description":"Aucune topologie déclarée"}},"tags":["donnees"]}},"/v1/projects/{pid}/iot":{"get":{"operationId":"lister_things","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Things du projet + état mesuré du broker MQTT"}},"tags":["iot"]},"post":{"operationId":"creer_thing","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewThingReq"}}},"required":true},"responses":{"201":{"description":"Thing créé — secret MQTT rendu UNE SEULE FOIS"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/composants":{"get":{"operationId":"lister_composants","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Composants Greengrass du projet (versions, jamais les artefacts en liste)"}},"tags":["iot"]},"post":{"operationId":"creer_composant","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewComposantReq"}}},"required":true},"responses":{"201":{"description":"Nouvelle version du composant (version auto = max+1)"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/deploiements":{"get":{"operationId":"lister_deploiements","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Déploiements Greengrass — statut DÉRIVÉ des ACK des devices"}},"tags":["iot"]},"post":{"operationId":"creer_deploiement","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewDeploiementReq"}}},"required":true},"responses":{"201":{"description":"Déploiement créé puis livré par MQTT — cibles EN_ATTENTE jusqu'à ACK"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/deploiements/{did}":{"delete":{"operationId":"supprimer_deploiement","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Déploiement retiré de l'historique"}},"tags":["iot"]},"get":{"operationId":"detail_deploiement","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Déploiement + état ACK de chaque cible"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/lot":{"post":{"operationId":"provisionner_lot_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotProvisionReq"}}},"required":true},"responses":{"201":{"description":"Lot provisionné : identités créées au registre, secrets clairs rendus UNE SEULE FOIS"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/lot/verifier":{"post":{"operationId":"verifier_lot_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifierLotReq"}}},"required":true},"responses":{"200":{"description":"Verdict : chaque identité vérifiée contre le registre (hash comparé) — manquantes nommées"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/regles":{"get":{"operationId":"lister_regles_iot","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles de routage MQTT de l'org"}},"tags":["iot"]},"post":{"operationId":"creer_regle_iot","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRegleIotReq"}}},"required":true},"responses":{"201":{"description":"Règle créée (validée par le module : comparateur connu, topic sous lab/)"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/regles/evaluer":{"post":{"operationId":"evaluer_regles_iot_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvaluerReglesIotReq"}}},"required":true},"responses":{"200":{"description":"Déclenchements des règles sur CE message (champ absent/non numérique → rien : on n'évalue pas l'illisible)"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/regles/{rid}":{"delete":{"operationId":"supprimer_regle_iot","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom de la règle","in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règle supprimée"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/statut":{"get":{"operationId":"statut_iot","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Broker MQTT : actif, port, compteurs mesurés"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/{tid}":{"delete":{"operationId":"supprimer_thing","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Thing supprimé (journal + shadow purgés)"}},"tags":["iot"]},"get":{"operationId":"detail_thing","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Thing + shadow versionné + 10 derniers messages"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/{tid}/publier":{"post":{"operationId":"publier_thing","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublierThingReq"}}},"required":true},"responses":{"200":{"description":"Message publié au thing par le broker réel"},"503":{"description":"Broker MQTT non démarré — jamais de succès déclaré"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/{tid}/shadow":{"get":{"operationId":"shadow_thing","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Shadow du thing — document JSON versionné"}},"tags":["iot"]},"patch":{"operationId":"maj_shadow_thing","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajShadowReq"}}},"required":true},"responses":{"200":{"description":"Desired fusionné — version incrémentée, publiée aux devices abonnés"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/{tid}/shadow/diff":{"get":{"operationId":"diff_shadow_thing","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Diff voulu/rapporté : à appliquer, divergents, orphelins rapportés, convergés — la convergence se CONSTATE"}},"tags":["iot"]}},"/v1/projects/{pid}/iot/{tid}/telemetrie":{"get":{"operationId":"telemetrie_thing","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Journal de télémétrie borné (200 messages)"}},"tags":["iot"]}},"/v1/projects/{pid}/ipam/historique":{"get":{"operationId":"historique_ipam_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Adresse IPv4 dont l'historique est demandé (a.b.c.d)","in":"query","name":"ip","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Historique d'une adresse dérivé du journal des baux (attribué/libéré OBSERVÉS) : détenteurs distincts, bornes observées, présent dérivé du DERNIER événement ; journal vide → rendu tel quel avec la note honnête — l'intégration y écrit à chaque événement réel, rien n'est reconstruit par déduction"},"400":{"description":"IP_REQUISE / IP_INVALIDE"}},"tags":["reseau"]}},"/v1/projects/{pid}/ipam/recherche":{"get":{"operationId":"recherche_ipam_globale","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Adresse IPv4 point-à-point cherchée (a.b.c.d)","in":"query","name":"ip","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Recherche GLOBALE : balaye les réseaux réels de l'org et rend une ligne par détenteur COURANT (baux DHCP ACTIFS — les expirés sont invisibles — et réservations durables, machines attachées comprises) ; une adresse que personne ne détient rend VIDE — dit, jamais deviné"},"400":{"description":"IP_REQUISE / IP_INVALIDE"}},"tags":["reseau"]}},"/v1/projects/{pid}/ipam/valider":{"post":{"operationId":"valider_reservation_ipam","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValiderReservationReq"}}},"required":true},"responses":{"200":{"description":"Verdict de réservation CONTRE les baux réels du réseau de l'org : Ok(()) quand l'adresse est réservable — IPv4 valide DANS le CIDR, jamais réseau/diffusion, jamais un bail DHCP ACTIF (nommé avec son détenteur), jamais une réservation existante ni la passerelle, jamais la plage DHCP dynamique"},"400":{"description":"RESERVATION_REFUSEE — refus NOMMÉ : hors CIDR, adresse de réseau/diffusion, bail actif (détenteur et expiration cités), déjà réservée, passerelle, plage DHCP dynamique"},"404":{"description":"Réseau inconnu dans l'org"}},"tags":["reseau"]}},"/v1/projects/{pid}/ipv6":{"get":{"operationId":"lister_ips_v6","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"IPs IPv6 affectées à l'organisation"}},"tags":["reseau"]},"post":{"operationId":"affecter_ip_v6","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffecterIpReq"}}},"required":true},"responses":{"200":{"description":"IP affectée (et appliquée sur la machine si Proxmox)"}},"tags":["reseau"]}},"/v1/projects/{pid}/ipv6/{aid}":{"delete":{"operationId":"liberer_ip_v6","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"IP libérée (et retirée de la machine si Proxmox)"}},"tags":["reseau"]}},"/v1/projects/{pid}/jobs":{"get":{"operationId":"lister_jobs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"File des jobs par lot — états réels, stats de file"}},"tags":["compute"]},"post":{"operationId":"soumettre_job","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewJobReq"}}},"required":true},"responses":{"201":{"description":"Job soumis en file — exécuté par le scheduler ou POST /executer"}},"tags":["compute"]}},"/v1/projects/{pid}/jobs/conteneur":{"post":{"operationId":"attacher_job_conteneur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobConteneurReq"}}},"required":true},"responses":{"201":{"description":"Descripteur validé et attaché au job : programme + argv EXACTS rendus à l'exécuteur (jamais assemblés en ligne de shell)"},"400":{"description":"CONTENEUR_INVALIDE — image non étiquetée, args, bornes mémoire/timeout"},"404":{"description":"Job inconnu du lot"}},"tags":["depots"]}},"/v1/projects/{pid}/jobs/dag":{"get":{"operationId":"lire_dag_jobs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Arcs posés + ORDRE TOPOLOGIQUE (cycles cités s'il y en a) + jobs PRÊTS à lancer (dus, tous prérequis reussis) + prérequis bloquants par job"},"410":{"description":"DAG_INCOHERENT — un cycle est apparu (jobs supprimés ?), le chemin est cité"}},"tags":["depots"]},"put":{"operationId":"poser_dag_jobs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DagReq"}}},"required":true},"responses":{"200":{"description":"Arcs remplacés — chaque arc cite deux jobs RÉELS et distincts de l'org (validé AVANT la pose)"},"400":{"description":"DAG_INVALIDE — arc trivial, job inconnu du lot, cap dépassé"}},"tags":["depots"]}},"/v1/projects/{pid}/jobs/fenetre":{"get":{"operationId":"lire_fenetre_jobs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Fenêtre réservée (ouverte ?) + jobs dus à la fermeture (préemption), 404 si aucune"}},"tags":["compute"]},"put":{"operationId":"poser_fenetre_jobs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FenetreReq"}}},"required":true},"responses":{"200":{"description":"Fenêtre d'exécution réservée posée (bornes RFC 3339 validées)"}},"tags":["compute"]}},"/v1/projects/{pid}/jobs/priorites":{"get":{"operationId":"lister_priorites_jobs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Priorités posées + ordre d'exécution dérivé (priorité PUIS FIFO de création)"}},"tags":["compute"]}},"/v1/projects/{pid}/jobs/{jid}":{"delete":{"operationId":"supprimer_job","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"jid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Job retiré — annulé s'il était en file"}},"tags":["compute"]},"get":{"operationId":"detail_job","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"jid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'un job — code de retour, sortie capturée, durée"}},"tags":["compute"]}},"/v1/projects/{pid}/jobs/{jid}/executer":{"post":{"operationId":"executer_job_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"jid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Job exécuté MAINTENANT — le handler attend la fin (plafonnée au timeout)"}},"tags":["compute"]}},"/v1/projects/{pid}/jobs/{jid}/preempter":{"post":{"operationId":"preempter_job_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"jid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Préemption PROPRE enregistrée (SIGTERM + grâce 30 s avant escalade)"}},"tags":["compute"]}},"/v1/projects/{pid}/jobs/{jid}/preempter/tick":{"post":{"operationId":"tick_preemption_job","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"jid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TickPreemptionReq"}}},"required":true},"responses":{"200":{"description":"La machine avance d'UNE observation — bilan MESURÉ quand l'arrêt est constaté"}},"tags":["compute"]}},"/v1/projects/{pid}/jobs/{jid}/priorite":{"delete":{"operationId":"retirer_priorite_job","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"jid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Priorité retirée (le job redevient NORMALE, le défaut nommé)"}},"tags":["compute"]},"put":{"operationId":"poser_priorite_job","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"jid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrioriteReq"}}},"required":true},"responses":{"200":{"description":"Priorité posée — la haute priorité CHOISIT en premier, elle ne fait pas doubler"}},"tags":["compute"]}},"/v1/projects/{pid}/k8s":{"get":{"operationId":"lister_k8s","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["kubernetes"]},"post":{"operationId":"creer_k8s","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewK8sReq"}}},"required":true},"responses":{"201":{"description":"Créé — k8s-vcluster : VRAI cluster virtuel déployé ; k8s-mini/k8s-ha/k8s-scale sans reel:true : enregistrement DECLARATIF (etat « declare », aucun nœud déployé) ; reel:true : nœuds LXC + k3s provisionnés (202) ; noeuds (FIN2-K8S-001) : surcharge le nombre de nœuds du plan (1..=10, refusé sur k8s-vcluster) ; la réponse embarque equivalents {cli, tofu} (FIN2-K8S-002)"},"400":{"description":"PLAN_REQUIS — le plan est obligatoire : k8s-vcluster (déploie un vrai vcluster) vs k8s-mini/k8s-ha/k8s-scale (déclaratifs) ; NOEUDS_INVALIDES / NOEUDS_INCOMPATIBLE — surcharge de nœuds hors bornes ou sur k8s-vcluster"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/addons":{"get":{"operationId":"catalogue_addons_k8s","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Catalogue des add-ons managés (charts officiels, versions ÉPINGLÉES) + dépendances système listées honnêtement"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/autopilot":{"get":{"operationId":"lire_autopilot_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Manifeste autopilot courant en vue SANS NŒUDS : pods + requêtes sommées — aucune information de nœud ne fuite"},"404":{"description":"Aucun manifeste posé"}},"tags":["kubernetes"]},"put":{"operationId":"poser_autopilot_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManifesteAutopilotApiReq"}}},"required":true},"responses":{"200":{"description":"Manifeste validé et posé : pods bornés, une réplique ne dépasse JAMAIS l'allocatable d'un nœud (un pod ne se découpe pas), bornes de pool 1 ≤ min ≤ max ≤ 10"},"400":{"description":"MANIFESTE_INVALIDE — chaque manquement nommé"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/autopilot/decision":{"post":{"operationId":"decider_autopilot_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionAutopilotReq"}}},"required":true},"responses":{"200":{"description":"Décision du pool implicite : requêtes sommées (mesuré), nœuds nécessaires DÉRIVÉS puis écrêtés par les bornes, action RELATIVE à l'état fourni (ajouter N | retirer N | rien)"},"404":{"description":"Aucun manifeste posé"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/cilium/etat":{"get":{"operationId":"etat_cilium_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"État RÉEL des pods Cilium (agents + operator, kube-system) lus du cluster — « jamais déployé » rendu tel ; harnais (CIRRO_WORKLOADS_REELS=0) non consulté, jamais simulé"},"503":{"description":"CLUSTER_INJOIGNABLE — le canal kubectl ne répond pas, aucun état inventé"}},"tags":["general"]}},"/v1/projects/{pid}/k8s/cilium/offre":{"get":{"operationId":"offre_cilium","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Offre honnête : Cilium 1.20.2 épinglé (Apache-2.0, dataplane eBPF) sur k3s — flannel coupé et network-policy désactivé à l'install, kube-proxy REMPLACÉ, prérequis LXC privilégiés cités, les trois commandes réelles du provisioning rendues, micro-segmentation par org (CiliumNetworkPolicy)"}},"tags":["general"]}},"/v1/projects/{pid}/k8s/federation":{"get":{"operationId":"vue_federee_k8s_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Vue fédérée des clusters RÉELS de l'org : totaux sommés, versions distinctes, incohérences nommées par cluster — les pods ne sont pas comptés par le plan de contrôle (dit tel), les nœuds et versions sont lus du store"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/federation/evaluer":{"post":{"operationId":"evaluer_federation_k8s","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClustersFederesReq"}}},"required":true},"responses":{"200":{"description":"Vue fédérée des clusters FOURNIS (pods/nœuds/versions mesurés par l'appelant) : référence = version la plus fréquente (première rencontrée à égalité), chaque cluster divergent nommé par nom ET id"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}":{"delete":{"operationId":"supprimer_k8s","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/addons":{"get":{"operationId":"addons_cluster_k8s","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Add-ons POSÉS sur le cluster + leur plan helm exact (aucun n'est exécuté ici)"},"404":{"description":"Cluster inconnu"}},"tags":["kubernetes"]},"post":{"operationId":"poser_addon_k8s","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddonReq"}}},"required":true},"responses":{"201":{"description":"Add-on inscrit : le PLAN helm exact est rendu (version épinglée, kubeconfig k3s) — JAMAIS exécuté ici"},"400":{"description":"ADDON_INCONNU — code hors catalogue"},"404":{"description":"Cluster inconnu"},"409":{"description":"DEJA_POSE — l'add-on est déjà inscrit sur ce cluster"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/kubeconfig":{"get":{"operationId":"kubeconfig_cluster","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/pools":{"get":{"operationId":"lister_pools","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["kubernetes"]},"post":{"operationId":"creer_pool","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewNodePoolReq"}}},"required":true},"responses":{"201":{"description":"Créé"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/pools/{npid}":{"delete":{"operationId":"supprimer_pool","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}},{"in":"path","name":"npid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/pools/{npid}/scale":{"post":{"operationId":"redimensionner_pool","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}},{"in":"path","name":"npid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScalePoolReq"}}},"required":true},"responses":{"200":{"description":"OK"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/scan":{"get":{"operationId":"scan_cve_cluster","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"responses":{"501":{"description":"SCAN_CVE_REEL_ABSENT — scan réel non implémenté : installez trivy et définissez CIRRO_TRIVY_BIN (le pipeline spawn existe). L'ancien pseudo-rapport à graine n'est plus servi (T15/A15)"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/services":{"get":{"operationId":"lister_services_k8s","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["kubernetes"]},"post":{"operationId":"creer_service_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewServiceK8sReq"}}},"required":true},"responses":{"201":{"description":"Créé"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/services/{sid}":{"delete":{"operationId":"supprimer_service_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/settings":{"get":{"operationId":"reglages_cluster","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["kubernetes"]},"put":{"operationId":"maj_reglages_cluster","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajReglagesReq"}}},"required":true},"responses":{"200":{"description":"OK"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/upgrade":{"post":{"operationId":"upgrade_cluster","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpgradeReq"}}},"required":true},"responses":{"200":{"description":"OK"}},"summary":"Monte le cluster d'une mineure. Cluster RÉEL : chaque nœud est drainé,\nl'installateur k3s épinglé sur la mineure cible est rejoué, le nœud\nattendu Ready puis ré-enrôlé — JAMAIS deux nœuds en même temps (le\nquorum etudiant le maintient). Cluster simulé : registre seul, dit le.","tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/upgrade-auto":{"get":{"operationId":"lire_upgrade_auto","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"État de la montée de version automatique (nœud courant, états, journal — tout MESURÉ)"},"404":{"description":"Cluster inconnu ou sans montée planifiée"}},"tags":["kubernetes"]},"post":{"operationId":"planifier_upgrade_auto_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpgradeAutoReq"}}},"required":true},"responses":{"201":{"description":"Montée planifiée (version ÉPINGLÉE, fenêtre non vide, nœuds nommés uniques) — aucun nœud n'est touché ici"},"400":{"description":"UPGRADE_INVALIDE — chaque manquement nommé"},"404":{"description":"Cluster inconnu"},"409":{"description":"DEJA_PLANIFIEE — une montée est déjà en cours"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/upgrade-auto/fenetre":{"put":{"operationId":"poser_fenetre_upgrade","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FenetreUpgradeApiReq"}}},"required":true},"responses":{"200":{"description":"Fenêtre remplacée — une fenêtre vide (début == fin) est refusée : une upgrade qui ne peut jamais jouer est un plan mensonger"},"404":{"description":"Cluster inconnu ou sans montée planifiée"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/upgrade-auto/tick":{"post":{"operationId":"tick_upgrade_auto","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TickUpgradeReq"}}},"required":true},"responses":{"200":{"description":"UN pas de la machine : la prochaine commande à exécuter (cordon/drain/upgrade/vérifier/uncordon — ou pause hors fenêtre, dit)"},"400":{"description":"OBSERVATION_INVALIDE — genre inconnu"},"404":{"description":"Cluster inconnu ou sans montée planifiée"}},"tags":["kubernetes"]}},"/v1/projects/{pid}/k8s/{kid}/volumes":{"get":{"operationId":"lister_volumes_k8s","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["stockage"]},"post":{"operationId":"monter_volume_k8s","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonterVolumeReq"}}},"required":true},"responses":{"201":{"description":"Créé"}},"summary":"Monte un volume du projet sur le cluster : Longhorn le expose comme PV\n(storage class par défaut), les pods le réclament par PVC.","tags":["stockage"]}},"/v1/projects/{pid}/k8s/{kid}/volumes/{vid}":{"delete":{"operationId":"demonter_volume_k8s","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["stockage"]}},"/v1/projects/{pid}/kv":{"get":{"operationId":"lister_tables_kv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tables kv de l'org avec leur compte honnête (vivantes, expirées non purgées, capacité, journal)"}},"tags":["donnees"]},"post":{"operationId":"creer_table_kv_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewTableKvReq"}}},"required":true},"responses":{"201":{"description":"Table kv créée, vide, compteurs à zéro"},"400":{"description":"TABLE_INVALIDE — nom hors bornes (1-60)"},"409":{"description":"NOM_PRIS — une table porte déjà ce nom"}},"tags":["donnees"]}},"/v1/projects/{pid}/kv/tikv/etat":{"get":{"operationId":"etat_tikv_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"État RÉEL des pods TiKV (opérateur + cluster tikv/pd) lus du cluster — « jamais déployé » rendu tel ; harnais (CIRRO_WORKLOADS_REELS=0) non consulté, jamais simulé"},"503":{"description":"CLUSTER_INJOIGNABLE — le canal kubectl ne répond pas, aucun état inventé"}},"tags":["donnees"]}},"/v1/projects/{pid}/kv/tikv/offre":{"get":{"operationId":"offre_tikv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Offre honnête : TiKV v8.5.0 LTS épinglé (Apache-2.0, CNCF gradué, Rust), fonctions mesurables, dépendances citées, mapping cirroKV→TiKV (TTL dit RESTE cirroKV), CR TiKVCluster rendu en entier + plan d'installation avec sa garde — version du chart opérateur en substitution explicite"}},"tags":["donnees"]}},"/v1/projects/{pid}/kv/{kvid}":{"delete":{"operationId":"supprimer_table_kv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kvid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Table supprimée (données et journal partent ensemble)"},"404":{"description":"Table inconnue"}},"tags":["donnees"]},"get":{"operationId":"detail_table_kv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kvid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Compte honnête + compteurs mesurés"},"404":{"description":"Table inconnue"}},"tags":["donnees"]}},"/v1/projects/{pid}/kv/{kvid}/cles/{cle}":{"delete":{"operationId":"supprimer_cle_kv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kvid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cle","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"supprime = true|false (absente ou déjà expirée — comptée comme expiration, pas comme suppression)"},"404":{"description":"Table inconnue"}},"tags":["donnees"]},"get":{"operationId":"lire_cle_kv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kvid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cle","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Valeur rendue — une entrée expirée part SUR LE COUP (comptée) et la lecture manque honnêtement (valeur null)"},"404":{"description":"Table inconnue"}},"tags":["donnees"]},"put":{"operationId":"poser_cle_kv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kvid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cle","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoserCleKvReq"}}},"required":true},"responses":{"200":{"description":"Clé posée — l'échéance d'expiration est rendue (null = sans TTL), l'événement « poser » est au journal"},"400":{"description":"ECRITURE_REFUSEE — clé/valeur/TTL hors bornes, ou capacité atteinte (le refus est compté)"},"404":{"description":"Table inconnue"}},"tags":["donnees"]}},"/v1/projects/{pid}/kv/{kvid}/consommer":{"post":{"operationId":"consommer_table_kv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kvid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsommerKvReq"}}},"required":true},"responses":{"200":{"description":"Événements postérieurs au CURSEUR du consommateur, puis curseur avancé — un événement n'est rendu qu'UNE fois par consommateur (dédup)"},"400":{"description":"LACUNE_JOURNAL — la rétention a évacué des événements avant consommation : refus nommé, jamais une consommation trouée"},"404":{"description":"Table inconnue"}},"tags":["donnees"]}},"/v1/projects/{pid}/kv/{kvid}/journal":{"get":{"operationId":"journal_table_kv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"kvid","required":true,"schema":{"type":"string"}},{"description":"Séquence strictement supérieure (0 = début retenu)","in":"query","name":"depuis","required":true,"schema":{"format":"int64","minimum":0,"type":"integer"}},{"description":"Plafond d'événements rendus","in":"query","name":"max","required":true,"schema":{"minimum":0,"type":"integer"}}],"responses":{"200":{"description":"Rejeu du journal (poser/supprimer/expirer) — best effort honnête : ce que la rétention a évacué n'est plus là"},"404":{"description":"Table inconnue"}},"tags":["donnees"]}},"/v1/projects/{pid}/llm-alias":{"get":{"operationId":"lister_alias","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["llm"]},"post":{"operationId":"creer_alias","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewAliasReq"}}},"required":true},"responses":{"201":{"description":"Créé"}},"tags":["llm"]}},"/v1/projects/{pid}/llm-alias/{aid}":{"delete":{"operationId":"supprimer_alias","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["llm"]}},"/v1/projects/{pid}/llm-endpoints":{"get":{"operationId":"lister_llm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["llm"]},"post":{"operationId":"creer_llm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewLlmEndpointReq"}}},"required":true},"responses":{"201":{"description":"Créé"}},"tags":["llm"]}},"/v1/projects/{pid}/llm-endpoints/{lid}":{"delete":{"operationId":"supprimer_llm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["llm"]}},"/v1/projects/{pid}/llm-endpoints/{lid}/alertes":{"get":{"operationId":"alertes_inference","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}},{"description":"seuil p95 de latence ms (défaut 5000)","in":"query","name":"p95_ms","required":false,"schema":{"format":"double","type":"number"}},{"description":"seuil de taux d'erreur 0.0-1.0 (défaut 0.10)","in":"query","name":"taux_erreur","required":false,"schema":{"format":"double","type":"number"}},{"description":"essais minimum avant d'alerter (défaut 10)","in":"query","name":"appels_min","required":false,"schema":{"format":"int64","minimum":0,"type":"integer"}}],"responses":{"200":{"description":"Alertes levées depuis les stats mesurées + échantillon et seuils réellement appliqués"}},"summary":"Alertes d'inférence (FLUX W-9) : les seuils configurables\n(`p95_ms`, `taux_erreur`, `appels_min` en query, défauts du module)\nsont évalués contre les StatsInference MESURÉES — un échantillon\ntrop maigre ne lève rien (dit honnêtement), chaque alerte levée\ncite le seuil ET la valeur mesurée.","tags":["general"]}},"/v1/projects/{pid}/llm-endpoints/{lid}/chat/completions":{"post":{"operationId":"chat_completions","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCompletionsReq"}}},"required":true},"responses":{"200":{"description":"OK"}},"summary":"`/v1/chat/completions` compatible OpenAI (EX-LLM-004).\nEndpoint suspendu (préempté) → 409 ; quota dépassé → 429.\nSi `llama_url` est configuré (llama-server externe, §6.7), la requête\ny est proxyfiée ; sinon la complétion est simulée mais déterministe.","tags":["llm"]}},"/v1/projects/{pid}/llm-endpoints/{lid}/creneaux":{"get":{"operationId":"lister_creneaux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["llm"]},"post":{"operationId":"creer_creneau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewCreneauReq"}}},"required":true},"responses":{"201":{"description":"Créé"}},"tags":["llm"]}},"/v1/projects/{pid}/llm-endpoints/{lid}/creneaux/{cid}":{"delete":{"operationId":"supprimer_creneau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["llm"]}},"/v1/projects/{pid}/llm-endpoints/{lid}/etat":{"post":{"operationId":"changer_etat_llm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"État changé"}},"summary":"Change l'état d'un endpoint LLM (suspendre/activer — préemption,\nmaintenance). P4-14 : le fallback d'alias s'appuie dessus.","tags":["llm"]}},"/v1/projects/{pid}/llm-endpoints/{lid}/metrics":{"get":{"operationId":"metrics_llm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"Métriques d'un endpoint : tokens, coût €, tok/s, créneaux (EX-LLM-007).","tags":["llm"]}},"/v1/projects/{pid}/llm-endpoints/{lid}/monitoring":{"delete":{"operationId":"reset_monitoring_inference","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Compteurs d'inférence remis à zéro"}},"tags":["general"]},"get":{"operationId":"monitoring_inference","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Stats d'inférence réelles de l'endpoint : appels, erreurs, p50/p95/p99, jetons, coût"}},"tags":["general"]}},"/v1/projects/{pid}/llm-endpoints/{lid}/openai/traduire":{"post":{"operationId":"traduire_openai","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Endpoint LLM qui sert la complétion","in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"Aller-retour COMPLET : requête OpenAI parsée → prompt interne → complétion → réponse AU FORMAT OpenAI (usage seulement s'il est mesuré) → relecture par le parseur client (le voyage est prouvé)"},"400":{"description":"REQUETE_OPENAI_INVALIDE — chaque incohérence citée avec sa position"},"404":{"description":"Endpoint inconnu ou suspendu"}},"tags":["llm"]}},"/v1/projects/{pid}/llm-endpoints/{lid}/rejeu/echantillonner":{"post":{"operationId":"echantillonner_rejeu_llm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EchantillonRejeuReq"}}},"required":true},"responses":{"200":{"description":"Échantillon DÉTERMINISTE (tri sha256 de la question — deux appels rendent le même échantillon)"}},"tags":["llm"]}},"/v1/projects/{pid}/llm-endpoints/{lid}/rejeu/juger":{"post":{"operationId":"juger_rejeu_llm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JugerRejeuReq"}}},"required":true},"responses":{"200":{"description":"Verdict stable|derive : similarités cosinus MESURÉES paire par paire, aucune réponse manquante tolérée"}},"tags":["llm"]}},"/v1/projects/{pid}/llm/ab/split":{"post":{"operationId":"split_ab_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SplitABReq"}}},"required":true},"responses":{"200":{"description":"Bras de l'identifiant : hash FNV-1a 32 (même idiome que la répartition L4 du dépôt) — STABLE, le même id finit TOUJOURS dans le même bras, et un id déjà en A y reste quand le pourcentage croît"},"400":{"description":"SPLIT_REFUSE — pourcentage hors 1-99 : à 0 ou 100 il n'y a qu'un bras, donc aucun test"}},"tags":["llm"]}},"/v1/projects/{pid}/llm/ab/verdict":{"post":{"operationId":"verdict_ab_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerdictABReq"}}},"required":true},"responses":{"200":{"description":"Verdict A/B sur compteurs MESURÉS : moyennes par bras (durée cumulée ÷ appels), écart absolu — GAGNANT nommé SEULEMENT si l'écart ≥ delta_min_ms, les deux bras mesurés et les taux d'erreur comparables (≤ 5 points), sinon INDECIS avec les chiffres qui manquent ; jamais de verdict sans écart significatif"},"400":{"description":"VERDICT_AB_REFUSE — corps illisible"}},"tags":["llm"]}},"/v1/projects/{pid}/llm/burst":{"get":{"operationId":"lire_alias_burst","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Alias de burst courant (cible publique + politique de fuite) — null tant qu'aucun n'est posé"}},"tags":["llm"]},"put":{"operationId":"poser_alias_burst","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AliasBurstApiReq"}}},"required":true},"responses":{"200":{"description":"Alias posé : cible externe déclarée (http/https), modèle servi, mots interdits = la politique de fuite (aucun ne quitte le lab)"},"400":{"description":"ALIAS_INVALIDE — nom, URL, modèle ou mots interdits hors clous, raisons nommées"}},"tags":["llm"]}},"/v1/projects/{pid}/llm/burst/arbitrer":{"post":{"operationId":"arbitrer_burst_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArbitrerBurstReq"}}},"required":true},"responses":{"200":{"description":"Destination tranchée : saturation MESURÉE d'abord (avec hystérésis — on y RESTE tant que la file ne redescend pas), politique de fuite ensuite — une requête qui fuite reste LOCALE même saturée, la raison le cite"},"400":{"description":"ARBITRAGE_REFUSE — seuils incohérents (retour ≥ bascule, sans hystérésis ça clignote)"},"404":{"description":"Aucun alias de burst posé"}},"tags":["llm"]}},"/v1/projects/{pid}/llm/cache-prefixes":{"delete":{"operationId":"vider_cache_prefixes","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Cache vidé (compteurs remis à zéro — un reset est un reset)"}},"tags":["llm"]},"get":{"operationId":"lire_cache_prefixes","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"État du cache de préfixes : entrées, hit/miss comptés, jetons et € épargnés CHIFFRÉS sur les tarifs du catalogue"}},"tags":["llm"]}},"/v1/projects/{pid}/llm/cache-prefixes/consulter":{"post":{"operationId":"consulter_cache_prefixes","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsulterCacheReq"}}},"required":true},"responses":{"200":{"description":"Verdict MESURÉ : HIT → les jetons du préfixe n'ont pas à être retraités, l'économie est comptée ; MISS → le préfixe entre au cache (éviction honnête du plus ancien)"}},"tags":["llm"]}},"/v1/projects/{pid}/llm/catalogue":{"get":{"operationId":"catalogue_llm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Catalogue ÉTENDU des moteurs par genre (chat, embeddings, rerank) : contexte maximal, coût showback €/1k tokens DÉRIVÉ de TARIFS_LLM pour les moteurs du lab (une seule source de vérité), notes de PROVENANCE qui citent la mesure (moteur lab / via proxy / showback estimé) + le tableau markdown publiable (nom | genre | contexte | €/1k | note)"}},"tags":["llm"]}},"/v1/projects/{pid}/llm/derive":{"post":{"operationId":"detecter_derive_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeriveReq"}}},"required":true},"responses":{"200":{"description":"Verdict de dérive sémantique : centroïde du RÉFÉRENTIEL contre centroïde de la FENÊTRE, divergence Jensen-Shannon bornée [0,1] en écart — STABLE ou DÉRIVE si l'écart DÉPASSE strictement le seuil (l'égalité n'est pas un dépassement), le détail cite les chiffres"},"400":{"description":"DERIVE_REFUSEE — refus NOMMÉ : seuil hors [0,1], référentiel ou fenêtre vide, empreintes de dimensions différentes ou non comparables (l'extraction d'embedding appartient au moteur)"}},"tags":["llm"]}},"/v1/projects/{pid}/llm/gpu/autoscale":{"get":{"operationId":"decision_autoscale_gpu","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Requêtes en attente — LA longueur mesurée","in":"query","name":"attente","required":true,"schema":{"minimum":0,"type":"integer"}},{"description":"Requêtes en cours de traitement","in":"query","name":"en_cours","required":true,"schema":{"minimum":0,"type":"integer"}},{"description":"Endpoints GPU actifs du pool","in":"query","name":"endpoints_actifs","required":true,"schema":{"minimum":0,"type":"integer"}},{"description":"Minutes d'inactivité prouvée (file ET traitement vides)","in":"query","name":"inactif_depuis_min","required":true,"schema":{"format":"int64","minimum":0,"type":"integer"}}],"responses":{"200":{"description":"Décision PURE et BORNÉE (démarrer/arrêter/rien — chaque branche CITE les nombres mesurés) ; aucune machine n'est touchée ici"},"400":{"description":"ETAT_INVALIDE — paramètres manquants ou illisibles"},"404":{"description":"AUCUNE_BORNES — posez les bornes du pool d'abord (PUT /llm/gpu/bornes)"}},"tags":["llm"]}},"/v1/projects/{pid}/llm/gpu/bornes":{"put":{"operationId":"poser_bornes_gpu","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BornesGpuReq"}}},"required":true},"responses":{"200":{"description":"Bornes du pool GPU posées — la décision ne sortira JAMAIS de [min, max]"},"400":{"description":"BORNES_INVALIDES — min ≥ 1, min ≤ max, seuil ≥ 1"}},"tags":["llm"]}},"/v1/projects/{pid}/loadbalancers":{"get":{"operationId":"lister_lbs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"cirroLB : équilibreurs de l'organisation (état + stats si actifs)"}},"tags":["reseau"]},"post":{"operationId":"creer_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewLbReq"}}},"required":true},"responses":{"201":{"description":"cirroLB : équilibreur créé — écoute son port si CIRROLB_TCP=1"}},"tags":["reseau"]}},"/v1/projects/{pid}/loadbalancers/ips":{"get":{"operationId":"ips_pool_lbs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pool d'IP d'écoute dédiées (CIRRO_LB_IPS) + IP libres (non portées par un équilibreur vivant, toutes organisations : le pool est global au processus)"}},"tags":["reseau"]}},"/v1/projects/{pid}/loadbalancers/offre":{"get":{"operationId":"offre_traefik","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Hôte frontal (ex. lb.cirro) — rend le docker run Traefik EXACT + la config TOML GÉNÉRÉE DES ÉQUILIBREURS RÉELS de l'org, jamais exécutés ici (cirrod lance et mesure)","in":"query","name":"hote","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Offre cirroLB Traefik : 3.7.13 épinglé (Apache-2.0), positionnement deux étages (maison = trafic INTERNE round-robin + sondes ; Traefik = trafic EXTERNE : TLS, routage par hôte), dashboard ÉTEINT par défaut ; avec ?hote= : docker_run_traefik + config_toml DÉRIVÉE des LB réels du registre (un équilibreur sans cible ne génère RIEN, dit en commentaire — aucun backend inventé) + garde verifier_plan_traefik"},"400":{"description":"PLAN_INSANE — le plan ne passe pas sa propre garde (image non épinglée, hôte absent, dashboard exposé)"}},"tags":["reseau"]}},"/v1/projects/{pid}/loadbalancers/{lid}":{"delete":{"operationId":"supprimer_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Équilibreur supprimé (serveur arrêté)"}},"tags":["reseau"]},"get":{"operationId":"detail_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'un équilibreur : config persistée (dont adresse_ip) + ip_ecoute effective + état/stats"},"404":{"description":"Équilibreur inconnu"}},"tags":["reseau"]}},"/v1/projects/{pid}/loadbalancers/{lid}/cibles":{"post":{"operationId":"ajouter_cible_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewCibleReq"}}},"required":true},"responses":{"201":{"description":"Cible ajoutée (prise en compte immédiatement)"}},"tags":["reseau"]}},"/v1/projects/{pid}/loadbalancers/{lid}/cibles/carte":{"post":{"operationId":"remplacer_cibles_carte_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CiblesCarteReq"}}},"required":true},"responses":{"200":{"description":"Cibles REMPLACÉES par les adresses IPv4 publiées du service (boucle refusée, carte vide refusée — jamais de cible inventée)"}},"tags":["reseau"]}},"/v1/projects/{pid}/loadbalancers/{lid}/listeners":{"get":{"operationId":"lister_listeners_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Listeners L7/L4 de l'équilibreur (L4 = relais d'octets TCP/UDP, modes round-robin | source-ip)"}},"tags":["reseau"]},"post":{"operationId":"creer_listener_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListenerReq"}}},"required":true},"responses":{"201":{"description":"Listener L4 créé (tcp|udp, port 1024-65535 libre, source-ip réservé au L4)"},"400":{"description":"Configuration invalide ou port déjà pris"}},"tags":["reseau"]}},"/v1/projects/{pid}/loadbalancers/{lid}/listeners/{lsid}":{"delete":{"operationId":"supprimer_listener_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lsid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Listener retiré"},"404":{"description":"Listener inconnu"}},"tags":["reseau"]}},"/v1/projects/{pid}/loadbalancers/{lid}/sonde":{"get":{"operationId":"lire_sonde_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Configuration de sonde + états PAR CIBLE (dériver des échecs/succès consécutifs mesurés) + pool effectif"}},"tags":["reseau"]},"put":{"operationId":"poser_sonde_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SondeActiveReq"}}},"required":true},"responses":{"200":{"description":"Sonde configurée (les résultats s'intègrent par POST /sonde/resultat)"},"400":{"description":"Configuration invalide"}},"tags":["reseau"]}},"/v1/projects/{pid}/loadbalancers/{lid}/sonde/resultat":{"post":{"operationId":"integrer_sonde_lb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"lid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultatSondeLbReq"}}},"required":true},"responses":{"200":{"description":"Résultat MESURÉ intégré (saine redérivé des seuils) + pool effectif après intégration"},"404":{"description":"Pas de sonde posée ou adresse hors des cibles"}},"tags":["reseau"]}},"/v1/projects/{pid}/logs":{"get":{"operationId":"chercher_logs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Sous-chaîne cherchée dans le corps (insensible à la casse)","in":"query","name":"texte","required":true,"schema":{"type":"string"}},{"description":"Composant exact","in":"query","name":"composant","required":true,"schema":{"type":"string"}},{"description":"Niveau exact (DEBUG, INFO, WARN, ERROR)","in":"query","name":"niveau","required":true,"schema":{"type":"string"}},{"description":"Trace exacte — rend la corrélation X-Trace-Id","in":"query","name":"trace_id","required":true,"schema":{"type":"string"}},{"description":"Numérotation à partir de 1","in":"query","name":"page","required":true,"schema":{"minimum":0,"type":"integer"}},{"description":"Taille de page (max 200)","in":"query","name":"taille","required":true,"schema":{"minimum":0,"type":"integer"}}],"responses":{"200":{"description":"Page de recherche : filtres en ET, total MESURÉ après filtre et AVANT pagination"},"400":{"description":"FILTRE_INVALIDE — filtre vide, niveau inconnu, page/taille hors bornes"}},"tags":["general"]}},"/v1/projects/{pid}/logs/chiffrer-lots":{"post":{"operationId":"chiffrer_lots_logs_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChiffrerLotsLogsReq"}}},"required":true},"responses":{"200":{"description":"Chiffre les lots CONSERVÉS du journal RÉEL de l'org (rendus par le journal, jamais simulés) : chaque lot sérialisé devient un objet AES-256-GCM (même moteur que le SSE des objets S3 — clé de données aléatoire enveloppée par la KEK de l'org), le lot EN CLAIR est DÉTRUIT réellement, et le flag meta logs_chiffres:{org} dit l'état ; les lignes ingérées APRÈS le chiffrement redeviennent claires — relancez pour les couvrir, DIT"},"400":{"description":"CHIFFREMENT_REFUSE — cle_racine_base64 absent/illisible/≠ 32 octets"}},"tags":["general"]}},"/v1/projects/{pid}/logs/ingester":{"post":{"operationId":"ingester_logs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngesterLogsReq"}}},"required":true},"responses":{"200":{"description":"Lot ingéré — validation REFUSANTE : niveau inconnu, composant/corps vide, trace_id hors contrat → refusé ET compté"},"400":{"description":"INGESTION_REFUSEE — la première ligne fautive nomme la cause"}},"tags":["general"]}},"/v1/projects/{pid}/logs/motifs":{"get":{"operationId":"lister_motifs_logs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles d'alerte sur motifs — N occurrences en M secondes, hystérésis persistée"}},"tags":["general"]},"post":{"operationId":"creer_motif_logs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewMotifLogReq"}}},"required":true},"responses":{"201":{"description":"Règle posée, état calme — la fenêtre est MESURÉE sur les horodatages réels des lignes"},"400":{"description":"MOTIF_INVALIDE — fenetre_s ≥ 1, seuil ≥ 1, hystérésis non nulle (seuil_retour < seuil)"},"409":{"description":"ID_PRIS"}},"tags":["general"]}},"/v1/projects/{pid}/logs/motifs/{rid}":{"delete":{"operationId":"supprimer_motif_logs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règle supprimée"},"404":{"description":"Règle inconnue"}},"tags":["general"]}},"/v1/projects/{pid}/logs/motifs/{rid}/evaluer":{"post":{"operationId":"evaluer_motif_logs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Évaluation sur le journal RÉEL : occurrences comptées dans la fenêtre, transitions rendues, hystérésis persistée entre évaluations"},"404":{"description":"Règle inconnue"}},"tags":["general"]}},"/v1/projects/{pid}/logs/retention":{"get":{"operationId":"lire_retention_logs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Politiques par composant + incidents ouverts — un composant SANS politique n'est JAMAIS purgé"}},"tags":["general"]},"put":{"operationId":"poser_retention_logs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetentionLogsReq"}}},"required":true},"responses":{"200":{"description":"Rétention posée : rétention ≥ 1 s, un seul politique par composant (ambiguïté refusée à l'exécution)"},"400":{"description":"RETENTION_INVALIDE — composant vide, rétention < 1 s"}},"tags":["general"]}},"/v1/projects/{pid}/logs/retention/purger":{"post":{"operationId":"purger_retention_logs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Purge exécutée : lignes purgées rendues dans l'ordre, la plus récente d'un composant sous incident PROTÉGÉE"},"400":{"description":"PURGE_REFUSEE — lot entier refusé si une politique est invalide ou ambiguë (jamais de purge partielle silencieuse)"},"404":{"description":"Aucune politique posée — sans politique, rien n'est purgé"}},"tags":["general"]}},"/v1/projects/{pid}/logs/traces/{trace_id}":{"get":{"operationId":"correler_logs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"trace_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Toutes les lignes de la trace, ordre chronologique d'ingestion"},"400":{"description":"TRACE_INVALIDE — format 8-64 (alphanumérique/-/_) : refusé, jamais normalisé"}},"tags":["general"]}},"/v1/projects/{pid}/machines":{"get":{"operationId":"lister_machines","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Machines réelles Proxmox + budget restant"}},"tags":["compute"]},"post":{"operationId":"creer_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewMachineReq"}}},"required":true},"responses":{"200":{"description":"Machine créée sur Proxmox (budget vérifié avant)"}},"tags":["compute"]}},"/v1/projects/{pid}/machines/{mid}":{"delete":{"operationId":"supprimer_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Machine détruite côté Proxmox et retirée du budget"}},"tags":["compute"]},"get":{"operationId":"detail_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'une machine du registre — dimensionnement, nœud, état"}},"summary":"Détail d'une machine par id — le PUT vit sur cette sous-ressource\ndepuis 0.81, le GET comble le patron « sous-ressource sans GET »\n(corrigé une première fois en 0.68 pour les plans de sauvegarde ;\ntrouvé en recette indépendante de la 0.81.0).","tags":["compute"]},"put":{"operationId":"modifier_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajMachineReq"}}},"required":true},"responses":{"200":{"description":"Machine modifiée sur Proxmox (vCPU/RAM/disque) et registre à jour"}},"summary":"Modifier une machine — comme la WebUI Proxmox → Ressources :\nPUT config PVE (vCPU/RAM) + resize (disque, croissance seule),\npuis registre resynchronisé sur la RÉALITÉ appliquée.","tags":["compute"]}},"/v1/projects/{pid}/machines/{mid}/action":{"post":{"operationId":"action_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MachineActionReq"}}},"required":true},"responses":{"200":{"description":"Action exécutée sur Proxmox (démarrer/arreter/redemarrer) — etat = statut LU sur PVE après l'action (tâches PVE asynchrones), jamais déduit"},"400":{"description":"Action inconnue, ou machine déclarative (MACHINE_DECLARATIVE)"},"503":{"description":"Harnais CIRRO_WORKLOADS_REELS=0 ou PVE non configuré — refus honnête, aucune simulation"}},"summary":"Action à la Proxmox sur une machine réelle (FIN2-MACH-001) :\ndemarrer/arreter/redemarrer via les primitives PVE. L'état rendu est\nLU sur PVE juste après (status/current) — une tâche start/stop PVE\nétant asynchrone, il peut encore transiter : c'est l'observation\nréelle, jamais la traduction de l'action demandée.","tags":["compute"]}},"/v1/projects/{pid}/machines/{mid}/backup":{"post":{"operationId":"backup_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Backup vzdump lancé (tâche asynchrone PVE)"}},"tags":["compute"]}},"/v1/projects/{pid}/machines/{mid}/backups":{"get":{"operationId":"backups_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Backups vzdump existants pour cette machine"}},"tags":["compute"]}},"/v1/projects/{pid}/machines/{mid}/firewall":{"get":{"operationId":"firewall_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles firewall PVE de la machine"}},"tags":["compute"]},"post":{"operationId":"ajouter_fw_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FwRuleReq"}}},"required":true},"responses":{"200":{"description":"Règle ajoutée au firewall PVE"}},"tags":["compute"]}},"/v1/projects/{pid}/machines/{mid}/ip":{"get":{"operationId":"ip_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"IPv4 de la machine : réservation IPAM si réseau attaché, sinon découverte live (LXC bail DHCP, VM agent qemu). ip: null = inconnue, jamais inventée"}},"summary":"IPv4 d'une machine réelle, par ordre de vérité : 1) la réservation\nIPAM si la machine est attachée à un réseau du projet (adresse\nposée à la création, source de référence) ; 2) la découverte live\ncôté Proxmox — bail DHCP d'un LXC démarré, agent qemu pour une VM.\n`ip: null` signifie honnêtement « pas encore connue » (machine\narrêtée, image sans agent qemu, bail pas encore posé).","tags":["compute"]}},"/v1/projects/{pid}/machines/{mid}/snapshots":{"get":{"operationId":"lister_snapshots_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Snapshots PVE RÉELS de la machine (liste live, pseudo-entrée « current » retirée)"},"400":{"description":"Machine déclarative (MACHINE_DECLARATIVE)"},"502":{"description":"PVE a refusé le listage — message remonté tel quel (PASSERELLE_PVE)"},"503":{"description":"Harnais CIRRO_WORKLOADS_REELS=0 ou PVE non configuré — jamais de liste inventée"}},"summary":"Liste les snapshots PVE réels d'une machine (FIN2-MACH-002) : liste\nlive lue sur PVE, jamais le registre — la pseudo-entrée « current »\n(« you are here ») est retirée par le client.","tags":["compute"]},"post":{"operationId":"creer_snapshot_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotReq"}}},"required":true},"responses":{"201":{"description":"Snapshot PVE créé (instantané disque, PAS un vzdump) — tâche PVE attendue, upid rendu"},"400":{"description":"Nom invalide (NOM_SNAPSHOT_INVALIDE) ou réservé par PVE (current/pending — NOM_SNAPSHOT_RESERVE), ou machine déclarative"},"502":{"description":"PVE a refusé (doublon de nom, verrou config…) — message PVE remonté tel quel (PASSERELLE_PVE)"},"503":{"description":"Harnais CIRRO_WORKLOADS_REELS=0 ou PVE non configuré — refus honnête, aucune simulation"}},"summary":"Crée un snapshot PVE (FIN2-MACH-002) : POST snapshot avec snap=<nom>,\ntâche PVE attendue (bornée) — instantané disque, pas un vzdump.","tags":["compute"]}},"/v1/projects/{pid}/machines/{mid}/snapshots/{nom}/restaurer":{"post":{"operationId":"restaurer_snapshot_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}},{"description":"Nom du snapshot cible","in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rollback PVE exécuté (tâche attendue, upid rendu)"},"400":{"description":"Nom invalide/réservé, ou machine déclarative"},"502":{"description":"PVE a refusé — p. ex. rollback d'un LXC DÉMARRÉ (« can't rollback, vm is running ») : le refus de PVE est remonté TEL QUEL (PASSERELLE_PVE), aucun pré-check inventé côté produit"},"503":{"description":"Harnais CIRRO_WORKLOADS_REELS=0 ou PVE non configuré — refus honnête, aucune simulation"}},"summary":"Restore une machine vers un snapshot PVE (FIN2-MACH-002) : POST\nsnapshot/<nom>/rollback. Aucun pré-check d'état : PVE exige lui-même\nun LXC arrêté et refuse sinon — son verdict remonte en 502\nPASSERELLE_PVE avec son message, sans mensonge.","tags":["compute"]}},"/v1/projects/{pid}/machines/{mid}/terminal":{"post":{"operationId":"terminal_machine","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TerminalReq"}}},"required":true},"responses":{"200":{"description":"Commande exécutée dans le conteneur"}},"summary":"Exécute une commande DANS le conteneur LXC via le nœud Proxmox.","tags":["compute"]}},"/v1/projects/{pid}/marketplace/veille":{"get":{"operationId":"veille_marketplace_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Veille semver sur les apps RÉELLES du catalogue étendu : installée = le tag ÉPINGLÉ du manifeste marketplace (ce que POST /marketplace déploie), disponible = la version épinglée par le produit quand une SECONDE source réelle existe (ntfy : l'offre cirroNotifications), sinon l'installée ; décision DÉRIVÉE (majeure → manuel, mineure/patch → auto-surveille, égales → à jour) — aucun flux amont n'est branché, la veille ne devine pas"}},"tags":["llm"]}},"/v1/projects/{pid}/marketplace/{app}/maj":{"post":{"operationId":"plan_maj_marketplace_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"App du catalogue marketplace (10 apps)","in":"path","name":"app","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajMarketplaceReq"}}},"required":true},"responses":{"200":{"description":"Plan de mise à jour RENDU : docker pull + le manifeste docker run ÉPINGLÉ sur la version EXACTE (le tag du catalogue substitué), JAMAIS latest, JAMAIS exécuté ici"},"400":{"description":"PLAN_MAJ_REFUSE — app hors catalogue, version floue/non semver, latest, app sans manifeste épinglé"}},"tags":["llm"]}},"/v1/projects/{pid}/materiel/gabarits":{"get":{"operationId":"catalogue_gabarits_materiel","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Catalogue de gabarits COMPLET : les six existants (miroir exact de driver::TYPES_VM) + familles spécialisées (cs-mem-*, cs-nvme-*, cs-gpu-a6000/l40s) ; le prix horaire n'est JAMAIS saisi — DÉRIVÉ des caractéristiques, et la formule redonne EXACTEMENT les tarifs existants de driver::TARIFS_VM (test de cohérence inter-modules)"}},"tags":["materiel"]}},"/v1/projects/{pid}/materiel/prevision":{"get":{"operationId":"prevision_capacite_materiel","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Horizon de projection en mois (1-24, défaut 6)","in":"query","name":"horizon","required":false,"schema":{"format":"int32","minimum":0,"type":"integer"}}],"responses":{"200":{"description":"Prévision de capacité par famille (vCPU, RAM Mo, disque Go) : disponible = budget effectif de l'org ( quotas), consommé = usage réel (machines du registre ∪ VMs déclaratives), projection écrêtée à [0, capacité] ; SANS historique mensuel mesuré la consommation courante est reconduite — la note le dit"},"400":{"description":"PREVISION_REFUSEE — horizon hors 1-24 mois, ou inventaire incohérent (consommé > disponible : nommé tel quel)"}},"tags":["materiel"]}},"/v1/projects/{pid}/materiel/right-sizing":{"post":{"operationId":"right_sizing_materiel","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RightSizingReq"}}},"required":true},"responses":{"200":{"description":"Le PLUS PETIT gabarit du catalogue couvrant la demande (CPU arrondi au vCPU supérieur, RAM avec marge 20 %, disque entier), sa RAISON lisible chiffre par chiffre et son prix DÉRIVÉ"},"400":{"description":"DEMANDE_IMPOSSIBLE — refus nommé : la dimension dépassée et le gabarit qui porte le maximum du catalogue"}},"tags":["materiel"]}},"/v1/projects/{pid}/metriques/alertes":{"get":{"operationId":"lister_regles_alerte","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles d'alerte de l'org avec leur phase courante (calme / alerte / resolu), bande d'hystérésis et compteur de transition en cours"}},"tags":["general"]},"post":{"operationId":"creer_regle_alerte_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRegleAlerteReq"}}},"required":true},"responses":{"201":{"description":"Règle créée (état de départ : calme, jamais daté — aucun échantillon vu)"},"400":{"description":"REGLE_INVALIDE — sens inconnu, durée nulle ou hystérésis nulle/inversée, raisons nommées"}},"tags":["general"]}},"/v1/projects/{pid}/metriques/alertes/{rid}":{"delete":{"operationId":"supprimer_regle_alerte","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"alr-…","in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règle retirée"},"404":{"description":"Règle inconnue"}},"tags":["general"]},"put":{"operationId":"maj_regle_alerte","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRegleAlerteReq"}}},"required":true},"responses":{"200":{"description":"Règle modifiée — l'état est REMIS À CALME (une nouvelle bande d'hystérésis n'hérite pas du compteur de l'ancienne)"},"400":{"description":"REGLE_INVALIDE — mêmes règles qu'à la création"},"404":{"description":"Règle inconnue"}},"tags":["general"]}},"/v1/projects/{pid}/metriques/alertes/{rid}/evaluer":{"post":{"operationId":"evaluer_regle_alerte_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/EchantillonReq"},"type":"array"}}},"required":true},"responses":{"200":{"description":"Lot évalué contre l'état PERSISTÉ (l'hystérésis est honnête entre deux lots) : nouvel état + transitions datées ; un pic isolé entre deux mesures calmes n'alerte pas, la résolution exige `duree` échantillons franchement calmes"},"404":{"description":"Règle inconnue"}},"tags":["general"]}},"/v1/projects/{pid}/metriques/dashboards":{"post":{"operationId":"generer_dashboard_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardReq"}}},"required":true},"responses":{"200":{"description":"Dashboard auto : UN panneau par série déclarée (doublons dédupliqués), requêtes rendues jamais exécutées — une série sans clé ou un dashboard sans série sont refusés (pas de widget vide)"},"400":{"description":"DASHBOARD_REFUSE — ressource vide, séries sans clé ou aucune série, raisons nommées"}},"tags":["general"]}},"/v1/projects/{pid}/metriques/slo":{"get":{"operationId":"calculer_slo_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Échantillons OK (mesuré)","in":"query","name":"ok","required":true,"schema":{"format":"int64","minimum":0,"type":"integer"}},{"description":"Échantillons totaux (mesuré)","in":"query","name":"total","required":true,"schema":{"format":"int64","minimum":0,"type":"integer"}},{"description":"Objectif de disponibilité en % — dans ]0, 100[ (à 100 % le budget d'erreur est nul)","in":"query","name":"objectif","required":true,"schema":{"format":"double","type":"number"}}],"responses":{"200":{"description":"SLI et budget d'erreur : dispo mesurée, budget total (1-objectif)×total, part consommée, verdict respecte/depasse — les deux gardes de division par zéro rendent des refus nommés"},"400":{"description":"SLO_REFUSE — aucun échantillon, ok > total (mesure incohérente, refusée plutôt que plafonnée), objectif hors ]0,100["}},"tags":["general"]}},"/v1/projects/{pid}/migration/checklist":{"get":{"operationId":"checklist_migration_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Checklist cochable (texte/markdown) : une ligne par étape, une sous-case par action, total estimé et manuel cité"},"404":{"description":"Aucune découverte mémorisée"}},"tags":["general"]}},"/v1/projects/{pid}/migration/decouverte":{"delete":{"operationId":"supprimer_decouverte_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Découverte supprimée — re-POST possible"},"404":{"description":"Aucune découverte mémorisée"}},"summary":"Supprime la découverte mémorisée (bug C14 (c), T15/A15) : le\nremplacement d'une découverte est un geste EXPLICITE — DELETE puis\nPOST. Sans découverte mémorisée : 404 honnête.","tags":["migration"]},"post":{"operationId":"decouvrir_csv_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"text/plain":{"schema":{"type":"string"}}},"description":"L'inventaire CSV TEXTE (colonnes nom,type,region,taille — en-tête détecté, inconnus nommés ligne à ligne)","required":true},"responses":{"200":{"description":"Découverte MESURÉE : types reconnus → gabarits cirro, inconnus NOMMÉS ligne à ligne, lignes malformées citées — mémorisée pour le plan et la checklist"},"400":{"description":"CSV_VIDE — un inventaire sans une seule ligne ne découvre rien"},"409":{"description":"DECOUVERTE_EXISTANTE — une découverte est déjà mémorisée : remplacer = DELETE puis POST (jamais d'écrasement silencieux, T15/A15)"}},"tags":["general"]}},"/v1/projects/{pid}/migration/plan":{"get":{"operationId":"plan_migration_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plan ordonné (bases → buckets → VMs → inconnus), effort estimé PAR TYPE, actions vérifiables"},"404":{"description":"Aucune découverte mémorisée — POST …/migration/decouverte d'abord"}},"tags":["general"]}},"/v1/projects/{pid}/migration/sondage":{"get":{"operationId":"sondage_migration","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"aws | gcp | azure — défaut : les trois","in":"query","name":"fournisseur","required":false,"schema":{"type":"string"}},{"description":"Familles à sonder, séparées par des virgules (ex. ec2/DescribeInstances,vms) — défaut : les services des quotas du fournisseur","in":"query","name":"familles","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Catalogue des fournisseurs sondables avec leurs quotas CONSERVATEURS, la durée de sondage ESTIMÉE par famille (appels estimés × délai au rythme saturé du quota — le pire cas honnête) et le total ; la PREUVE réelle est DIFFÉRÉE et DITE (décision 6.c du 02/10 : identifiants lecture-seule à fournir, la logique est prête)"},"400":{"description":"SONDAGE_REFUSE — fournisseur inconnu (aws | gcp | azure)"}},"tags":["equivalents"]}},"/v1/projects/{pid}/modeles":{"get":{"operationId":"lister_modeles","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Modèles enregistrés et leurs versions"}},"tags":["general"]},"post":{"operationId":"creer_modele","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewModeleReq"}}},"required":true},"responses":{"201":{"description":"Modèle créé (registre vide de versions)"}},"tags":["general"]}},"/v1/projects/{pid}/modeles/datasets":{"get":{"operationId":"lister_datasets_modeles","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Datasets versionnés (chaque version porte l'empreinte sha256 de SES octets — la preuve)"}},"tags":["llm"]},"post":{"operationId":"creer_dataset_modeles","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewDatasetReq"}}},"required":true},"responses":{"201":{"description":"Dataset créé (les versions s'enregistrent ensuite, empreinte MESURÉE à l'appui)"},"400":{"description":"Nom invalide"},"409":{"description":"Nom déjà pris"}},"tags":["llm"]}},"/v1/projects/{pid}/modeles/datasets/attacher":{"post":{"operationId":"attacher_dataset_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttacherDatasetReq"}}},"required":true},"responses":{"200":{"description":"Lien posé dataset↔version de modèle (le modèle DOIT exister avec SA version, le dataset avec la sienne)"},"400":{"description":"Référence mal formée ou lien déjà posé"}},"tags":["llm"]}},"/v1/projects/{pid}/modeles/datasets/lineage":{"get":{"operationId":"lineage_datasets_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lineage : chaque version de dataset avec TOUTES les versions de modèle nourries (liens orphelins rendus avec dataset_present: false)"}},"tags":["llm"]}},"/v1/projects/{pid}/modeles/datasets/{did}":{"delete":{"operationId":"supprimer_dataset_modeles","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Dataset supprimé — les liens deviennent orphelins, le lineage les rend avec dataset_present: false (dit, pas caché)"},"404":{"description":"Dataset inconnu"}},"tags":["llm"]},"get":{"operationId":"detail_dataset_modeles","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Dataset + versions immuables (empreinte, provenance, exemples comptés)"},"404":{"description":"Dataset inconnu"}},"tags":["llm"]}},"/v1/projects/{pid}/modeles/datasets/{did}/versions":{"post":{"operationId":"creer_version_dataset","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"did","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewVersionDatasetReq"}}},"required":true},"responses":{"201":{"description":"Version enregistrée (numéro séquentiel, immuable — empreinte sha256 MESURÉE par l'intégrateur sur les octets réels)"},"400":{"description":"Empreinte/provenance/exemples invalides"}},"tags":["llm"]}},"/v1/projects/{pid}/modeles/signature":{"get":{"operationId":"contrat_signature_modeles","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Contrat de signature d'artefacts : Ed25519 (MÊME moteur que les bundles export — crate ed25519-compact, seed 32 octets, signature hex minuscule), la granularité est l'ARTEFACT (chaque empreinte sha256 se signe et se revérifie seule), le manifeste est DÉTERMINISTE (trié par nom) et RENDU — JAMAIS signé côté store : la clé privée ne transite pas par l'API"}},"tags":["llm"]}},"/v1/projects/{pid}/modeles/{mid}":{"delete":{"operationId":"supprimer_modele","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Modèle et ses versions supprimés"}},"tags":["general"]}},"/v1/projects/{pid}/modeles/{mid}/affinages":{"post":{"operationId":"lancer_affinage","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffinerReq"}}},"required":true},"responses":{"202":{"description":"Affinage lancé (tâche de fond, journal consultable)"},"400":{"description":"Base/paramètres refusés (version inconnue, artefact non local, exemples hors bornes)"},"409":{"description":"Un affinage est déjà en cours dans l'org"},"503":{"description":"Aucun entraîneur installé — jamais de fausses métriques"}},"tags":["general"]}},"/v1/projects/{pid}/modeles/{mid}/transition":{"post":{"operationId":"transition_modele","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromouvoirReq"}}},"required":true},"responses":{"200":{"description":"Transition appliquée (prod unique, rollback = re-promouvoir)"}},"tags":["general"]}},"/v1/projects/{pid}/modeles/{mid}/versions":{"post":{"operationId":"creer_version_modele","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewVersionReq"}}},"required":true},"responses":{"201":{"description":"Version enregistrée (brouillon)"}},"tags":["general"]}},"/v1/projects/{pid}/modeles/{mid}/versions/{version}":{"get":{"operationId":"detail_version_modele","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}},{"in":"path","name":"version","required":true,"schema":{"format":"int32","minimum":0,"type":"integer"}}],"responses":{"200":{"description":"Version + évaluations AGRÉGÉES (pondérées par le nombre de cas de chaque suite — le total des réussites sur le total des cas)"},"404":{"description":"Modèle ou version inconnue"}},"tags":["general"]}},"/v1/projects/{pid}/modeles/{mid}/versions/{version}/evals":{"post":{"operationId":"attacher_eval_version","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}},{"in":"path","name":"version","required":true,"schema":{"format":"int32","minimum":0,"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttacherEvalReq"}}},"required":true},"responses":{"201":{"description":"Éval MESURÉE attachée à la version (score, cas, manques — comptés sur l'exécution réelle)"},"400":{"description":"EVAL_INVALIDE — version inconnue, exécution sans résultat, doublon"},"404":{"description":"Modèle ou exécution inconnue"}},"tags":["general"]}},"/v1/projects/{pid}/modeles/{mid}/versions/{version}/transition-approuvee":{"post":{"operationId":"transition_approuvee_version","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}},{"in":"path","name":"version","required":true,"schema":{"format":"int32","minimum":0,"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransitionApprouveeReq"}}},"required":true},"responses":{"200":{"description":"Transition faite (staging/archive sans validation — le contrôle vise la prod)"},"404":{"description":"Modèle ou version inconnue"},"409":{"description":"VALIDATION_EXIGEE — promotion en prod sans validation humaine enregistrée : le refus dit quoi poser"}},"tags":["general"]}},"/v1/projects/{pid}/modeles/{mid}/versions/{version}/validation":{"post":{"operationId":"enregistrer_validation_version","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}},{"in":"path","name":"version","required":true,"schema":{"format":"int32","minimum":0,"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationHumaineReq"}}},"required":true},"responses":{"201":{"description":"Validation ENREGISTRÉE (qui/quand/commentaire) — elle déverrouille la transition vers prod de CETTE version"},"400":{"description":"VALIDATION_INVALIDE — les trois champs exigés, un par un"},"404":{"description":"Modèle ou version inconnue"},"409":{"description":"DEJA_VALIDEE — une version ne se valide qu'UNE fois"}},"tags":["general"]}},"/v1/projects/{pid}/modeles/{mid}/versions/{vid}/signer":{"post":{"operationId":"signer_version_modele_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Modèle (mdl-…)","in":"path","name":"mid","required":true,"schema":{"type":"string"}},{"description":"Version du modèle","in":"path","name":"vid","required":true,"schema":{"format":"int32","minimum":0,"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignerVersionReq"}}},"required":true},"responses":{"200":{"description":"Manifeste DÉTERMINISTE rendu (trié par nom, dédupliqué) + la marche à suivre : chaque empreinte se signe LOCALEMENT par signer_artefact (seed Ed25519 32 octets) et se vérifie par verifier_signature_artefact — la clé privée ne transite JAMAIS par l'API, rien n'est signé ni stocké ici"},"400":{"description":"SIGNATURE_REFUSEE — empreinte non sha256 (64 hex minuscules), nom vide, aucun artefact fourni"},"404":{"description":"Modèle ou version inconnu de l'org"}},"tags":["llm"]}},"/v1/projects/{pid}/monitoring":{"delete":{"operationId":"supprimer_monitoring","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Stack supprimé par étiquette — rien d'orphelin"}},"tags":["gouvernance"]},"get":{"operationId":"etat_monitoring","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"État réel du stack (pods lus dans le cluster) — 404 si jamais déployé"}},"summary":"État RÉEL lu dans le cluster (pas le registre seul) : pods, âge,\nrestarts — honnête même si quelqu'un a touché le cluster à la main.","tags":["gouvernance"]},"post":{"operationId":"deployer_monitoring","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Prometheus + Grafana déployés dans le namespace de l'org, sur ses ressources"}},"summary":"Déploie la stack de surveillance DE l'organisation : Prometheus\n(scrape les pods annotés du namespace + cAdvisor) et Grafana\n(datasource + dashboard « Vue d'organisation » provisionnés).\nIdempotent : re-apply sans destruction.","tags":["gouvernance"]}},"/v1/projects/{pid}/nat":{"get":{"operationId":"lister_regles_nat","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK — règles conformes uniquement ; les héritées sans cible sont comptées mais non servies (champs additifs heritees/note)"}},"tags":["reseau"]},"post":{"operationId":"creer_regle_nat","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRegleNatReq"}}},"required":true},"responses":{"201":{"description":"Créé — règle nft APPLIQUÉE au noyau du nœud (etat « appliquee »)"},"400":{"description":"Requête invalide (ports/sens)"},"404":{"description":"VM cible introuvable"},"422":{"description":"cible sans IP réelle — la règle resterait sans effet"}},"tags":["reseau"]}},"/v1/projects/{pid}/nat/{nid}":{"delete":{"operationId":"supprimer_regle_nat","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé — règle nft retirée du noyau (idempotent)"},"404":{"description":"Règle NAT introuvable"},"500":{"description":"Échec du retrait nft — l'objet est conservé (pas de dérive noyau/registre)"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks":{"get":{"operationId":"lister_reseaux","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Réseaux définis du projet"}},"tags":["reseau"]},"post":{"operationId":"creer_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewReseauReq"}}},"required":true},"responses":{"201":{"description":"Réseau défini (CIDR, DHCP optionnel)"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/ipam/export.csv":{"get":{"operationId":"exporter_ipam_csv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Inventaire d'adressage complet : une ligne PAR ADRESSE (machine, bail, réservation, passerelle ou libre)"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/ipam/occupation":{"get":{"operationId":"occupation_ipam","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Seuil d'alerte d'occupation en % (défaut 90)","in":"query","name":"seuil","required":false,"schema":{"format":"int32","minimum":0,"type":"integer"}}],"responses":{"200":{"description":"Occupation MESURÉE du pool de chaque réseau (les baux expirés ne comptent pas) + alertes de pools presque pleins"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/mesh":{"get":{"operationId":"lire_mesh_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Topologie mesh inter-sites (sites, préfixes annoncés, liaisons) — la table de routage se DÉRIVE de cette topologie"}},"tags":["reseau"]},"put":{"operationId":"poser_mesh_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopologieMeshReq"}}},"required":true},"responses":{"200":{"description":"Topologie validée et posée — TOUS les refus sont rendus (préfixes chevauchants, liaisons dupliquées, boucles)"},"400":{"description":"TOPOLOGIE_INVALIDE — chaque erreur est listée, aucune pose partielle"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/mesh/decision":{"post":{"operationId":"decider_routage_mesh_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionMeshReq"}}},"required":true},"responses":{"200":{"description":"Verdict de routage : site qui dessert l'IP (plus long préfixe), chemin le plus court (BFS), premier saut — refus nommés sinon"},"404":{"description":"AUCUNE_TOPOLOGIE — posez la topologie d'abord"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/routage":{"get":{"operationId":"lister_tables_routage","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tables de routage par sous-réseau (une destination = une passerelle, ambiguïté refusée à l'écriture)"}},"tags":["reseau"]},"put":{"operationId":"poser_table_routage_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TableRoutage"}}},"required":true},"responses":{"200":{"description":"Table créée ou REMPLACÉE pour ce sous-réseau (chaque route : passerelle joignable depuis le sous-réseau, destination unique)"},"400":{"description":"Route invalide, passerelle hors sous-réseau ou ambiguïté"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/routage/decision":{"get":{"operationId":"decider_route_table","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"query","name":"sous_reseau","required":true,"schema":{"type":"string"}},{"description":"IPv4 à router","in":"query","name":"destination","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Verdict PUR : plus long préfixe gagne, aucune correspondance = None honnête"},"400":{"description":"Destination non IPv4 ou table ambiguë au meilleur préfixe"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/{nid}":{"delete":{"operationId":"supprimer_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Réseau supprimé"}},"tags":["reseau"]},"get":{"operationId":"detail_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail : IPs prises, capacité, baux"}},"tags":["reseau"]},"put":{"operationId":"maj_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajReseauReq"}}},"required":true},"responses":{"200":{"description":"DHCP activé/désactivé"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/{nid}/adresses":{"get":{"operationId":"lister_adresses_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Adresses prises (machines, baux, réservations) + libres du pool IPAM"}},"tags":["reseau"]},"post":{"operationId":"attacher_machine_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttacherReq"}}},"required":true},"responses":{"201":{"description":"Machine attachée : adresse réservée dans l'IPAM, posée sur Proxmox quand c'est possible"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/{nid}/adresses/{ip}":{"delete":{"operationId":"detacher_adresse_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}},{"in":"path","name":"ip","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Machine détachée : adresse rendue au pool (et net0 remise en DHCP si Proxmox)"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/{nid}/firewall":{"get":{"operationId":"etat_firewall_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles ordonnées + statut par défaut du réseau"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/{nid}/firewall/defaut":{"post":{"operationId":"definir_defaut_fe","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DefautFeReq"}}},"required":true},"responses":{"200":{"description":"Statut par défaut redéfini (trafic sans règle correspondante)"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/{nid}/firewall/regles":{"post":{"operationId":"creer_regle_fe","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRegleFeReq"}}},"required":true},"responses":{"201":{"description":"Règle ajoutée EN QUEUE (évaluée après les existantes)"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/{nid}/firewall/regles/{rid}":{"delete":{"operationId":"supprimer_regle_fe","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règle retirée"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/{nid}/firewall/test":{"post":{"operationId":"tester_firewall_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestFeReq"}}},"required":true},"responses":{"200":{"description":"Verdict : statut + règle responsable (ou défaut)"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/{nid}/flows":{"get":{"operationId":"lister_flows_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Journal du réseau + agrégats par paire (top talkers MESURÉS — le vide rend vide)"}},"tags":["reseau"]},"post":{"operationId":"journaliser_flow_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvenementFlowReq"}}},"required":true},"responses":{"201":{"description":"Événement VALIDÉ puis journalisé (borné — les plus vieux partent)"},"400":{"description":"Mesure incohérente (protocole, IPs, ports, octets/paquets)"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/{nid}/lease":{"post":{"operationId":"bail_reseau","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BailReq"}}},"required":true},"responses":{"201":{"description":"Bail DHCP attribué (première IP libre de la plage)"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/{nid}/reserve":{"post":{"operationId":"reserver_ip","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReservationReq"}}},"required":true},"responses":{"201":{"description":"IP réservée (statique)"}},"tags":["reseau"]}},"/v1/projects/{pid}/networks/{nid}/reserve/{ip}":{"delete":{"operationId":"liberer_ip","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}},{"in":"path","name":"ip","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Réservation/libération d'IP"}},"tags":["reseau"]}},"/v1/projects/{pid}/nfs":{"get":{"operationId":"lister_nfs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Partages NFS de l'organisation — chaque item porte son `nodeport` et son `etat` (actif = pod serveur prêt sur 2049)"}},"tags":["donnees"]},"post":{"operationId":"creer_nfs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewPartageReq"}}},"required":true},"responses":{"201":{"description":"Partage NFS créé : VRAI pod serveur NFS (nfs-ganesha, NFSv4/2049) + PVC local-path sur le cluster plateforme, Service NodePort. La réponse porte `nodeport` et `commande_mount` (mount -t nfs4 -o nfsvers=4,port=<nodeport>,tcp <ip>:/ ...) — montable depuis le LAN. `etat` = actif (rollout terminé, readinessProbe 2049)"}},"tags":["donnees"]}},"/v1/projects/{pid}/nfs/agreger":{"post":{"operationId":"agreger_partages_nfs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgregerNfsReq"}}},"required":true},"responses":{"201":{"description":"Manifeste d'agrégation scale-out : 2+ partages RÉELS de l'org derrière un point de montage unique, un membre par partage avec son préfixe (/membre-1, /membre-2…), capacité = SOMME des tailles MESURÉES, politique « écriture = premier membre, lecture = tous » ; le manifeste DÉCRIT, le montage réel (referrals NFSv4 / fuse) appartient à l'intégration — DIT"},"400":{"description":"AGREGATION_REFUSEE — refus NOMMÉ : moins de 2 membres (un seul partage n'est pas du scale-out), point non absolu, membre dupliqué"},"404":{"description":"Membre inconnu — l'agrégat ne se construit que sur les partages RÉELS de l'org (id cité)"}},"tags":["donnees"]}},"/v1/projects/{pid}/nfs/coherence":{"get":{"operationId":"coherence_agregat_nfs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Point de montage unique de l'agrégat (ex. /mnt/agrege-donnees)","in":"query","name":"point","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Verdict de cohérence de l'agrégat : signatures de contenu MESURÉES des membres (journal tenu par l'intégration, même ordre que le manifeste) → Coherent (toutes égales) ou Ecart NOMMÉ (l'agrégat ne réplique pas, il distribue — le membre divergent est signalé, jamais fusionné) ; sans signature mesurée, le CONTRAT est rendu — aucun verdict décrété sur une mesure absente"},"400":{"description":"POINT_REQUIS — le paramètre ?point= est exigé"},"404":{"description":"Aucun agrégat sur ce point — POST /nfs/agreger le construit"}},"tags":["donnees"]}},"/v1/projects/{pid}/nfs/{nid}":{"delete":{"operationId":"supprimer_nfs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Partage supprimé : pod serveur, Service, netpol, ConfigMap et PVC retirés par étiquette (rien d'orphelin), ligne de registre retirée"}},"tags":["donnees"]}},"/v1/projects/{pid}/nfs/{nid}/smb":{"delete":{"operationId":"retirer_option_smb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Option SMB retirée"}},"tags":["donnees"]},"get":{"operationId":"lire_option_smb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Option SMB + avertissement honnête (NFSv4 uniquement)"},"404":{"description":"Aucune option SMB posée"}},"tags":["donnees"]},"put":{"operationId":"poser_option_smb","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OptionSmbReq"}}},"required":true},"responses":{"200":{"description":"Option SMB posée — le nom est RÉSERVÉ (annotations du manifeste), aucune passerelle Samba n'est déployée"}},"tags":["donnees"]}},"/v1/projects/{pid}/nfs/{nid}/snapshots":{"get":{"operationId":"lister_snapshots_nfs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Snapshots du partage (les plus récents d'abord)"}},"tags":["donnees"]},"post":{"operationId":"creer_snapshot_nfs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreerSnapshotNfsReq"}}},"required":true},"responses":{"201":{"description":"Snapshot capturé (relevé validé, jamais tronqué) + rotation observée"}},"tags":["donnees"]}},"/v1/projects/{pid}/nfs/{nid}/snapshots/fichier":{"get":{"operationId":"retrouver_fichier_nfs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}},{"description":"Chemin relatif du fichier cherché","in":"query","name":"chemin","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Occurrences du fichier dans les snapshots, de la plus récente à la plus ancienne"}},"tags":["donnees"]}},"/v1/projects/{pid}/nfs/{nid}/snapshots/restauration":{"post":{"operationId":"plan_restauration_nfs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanRestaurationNfsReq"}}},"required":true},"responses":{"200":{"description":"PLAN de restauration (source sous .snapshots/, destination, écrasement dit) — la copie réelle reste à l'opérateur"}},"tags":["donnees"]}},"/v1/projects/{pid}/nftables":{"get":{"operationId":"config_nftables","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"Configuration nftables compilée du projet — vue LISIBLE ; les objets\nNAT sont en réalité appliqués au noyau UN PAR UN à leur création\n(anomalie 10 : cette vue n'est plus le seul endroit où la conf vit).","tags":["reseau"]}},"/v1/projects/{pid}/notifications/canaux":{"get":{"operationId":"canaux_notifications_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Catalogue des canaux de notification : ntfy LIVRÉ (push téléphone, POST https://{hote}/{topic}), matrix VIA PONT (canal_push matrix:{room}, slug 2-40 — le pont Matrix EXISTANT des agents publie, le format est validé à la pose du routage), webhook SIGNÉ (HMAC, livraison existante), xmpp REFUSÉ NOMMÉ (arbitrage 5.a/6.c du 02/10 en attente)"}},"tags":["general"]}},"/v1/projects/{pid}/notifications/ntfy/offre":{"get":{"operationId":"offre_ntfy","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Nom DNS du serveur ntfy (ex. push.cirro) — rend le plan de déploiement EXACT et le topic de démonstration","in":"query","name":"hote","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Offre honnête : ntfy épinglé (Apache-2.0, push iOS/Android/desktop sans compte), fonctions mesurables, dépendances citées, plan docker exact si ?hote=, mapping topics cirro→ntfy, commande de publication, santé /v1/health"},"400":{"description":"HOTE_INVALIDE / PLAN_INSANE — nom DNS seul (sans https://), ou plan ne passant pas sa propre garde"}},"tags":["general"]}},"/v1/projects/{pid}/observabilite/sentry/offre":{"get":{"operationId":"offre_sentry","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Tag du self-hosted à poser (ex. 26.1.0) — versionnage calendaire, dernier tag sur github.com/getsentry/self-hosted/releases ; rend le plan EXACT","in":"query","name":"tag","required":true,"schema":{"type":"string"}},{"description":"Nom DNS de l'instance (ex. erreurs.cirro)","in":"query","name":"hote","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Offre honnête : Sentry self-hosted (licence FSL-1.1-MIT DITE — auto-hébergement libre, MIT après 2 ans) + alternative GlitchTip (MIT, compatible DSN), fonctions mesurables, dépendances citées (~16 Go RAM DITS), plan exact si ?tag=&hote= — la garde refuse une branche flottante"},"400":{"description":"TAG_REQUIS / HOTE_INVALIDE / PLAN_INSANE — tag calendaire explicite exigé, ou plan ne passant pas sa propre garde"}},"tags":["general"]}},"/v1/projects/{pid}/passerelle/transformations":{"get":{"operationId":"lister_transformations","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles de transformation posées (préfixes uniques — l'ordre reste déterministe)"}},"tags":["passerelle"]},"put":{"operationId":"poser_transformations","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegleTransformationReqArray"}}},"required":true},"responses":{"200":{"description":"Jeu de règles remplacé (chaque règle valide, noms et préfixes UNIQUES — la PREMIÈRE règle qui couvre gagne)"},"400":{"description":"REGLES_INVALIDES — chaque manquement nommé"}},"tags":["passerelle"]}},"/v1/projects/{pid}/passerelle/transformations/{nom}":{"delete":{"operationId":"supprimer_transformation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règle retirée"},"404":{"description":"Règle inconnue"}},"tags":["passerelle"]}},"/v1/projects/{pid}/passerelle/transformer":{"post":{"operationId":"transformer_requete_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequeteAbstraiteReq"}}},"required":true},"responses":{"200":{"description":"Requête TRANSFORMÉE : la première règle qui couvre réécrit le chemin (préfixe REMPLACÉ, reste conservé), retire puis pose les en-têtes ; aucune règle ne matche : requête intacte, dit tel quel"},"400":{"description":"TRANSFORMATION_REFUSEE — le jeu de règles lui-même est invalide, rien ne s'applique en silence"}},"tags":["passerelle"]}},"/v1/projects/{pid}/pinpoint":{"get":{"operationId":"lire_pinpoint","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Segments + campagnes (résumés réels : livrés/échecs, aucun taux inventé)"}},"tags":["notifications"]}},"/v1/projects/{pid}/pinpoint/attributs/{email}":{"get":{"operationId":"lire_attributs_membre","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Membre de l'org","in":"path","name":"email","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Attributs du membre (carte clé → valeur, vide si aucun)"},"404":{"description":"Membre inconnu de l'org"}},"tags":["notifications"]},"put":{"operationId":"poser_attributs_membre","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Membre de l'org","in":"path","name":"email","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoserAttributsReq"}}},"required":true},"responses":{"200":{"description":"Attributs posés — les segments à prédicats les reliront À L'ENVOI"},"404":{"description":"Membre inconnu de l'org"}},"tags":["notifications"]}},"/v1/projects/{pid}/pinpoint/campagnes":{"post":{"operationId":"creer_campagne_pinpoint","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewCampagneReq"}}},"required":true},"responses":{"201":{"description":"Campagne en brouillon (envoi unique, duplication possible)"}},"tags":["notifications"]}},"/v1/projects/{pid}/pinpoint/campagnes/{cid}":{"delete":{"operationId":"supprimer_campagne_pinpoint","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Campagne retirée (journal conservé)"}},"tags":["notifications"]},"get":{"operationId":"detail_campagne_pinpoint","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Campagne + journal d'envoi (lignes réelles)"}},"tags":["notifications"]}},"/v1/projects/{pid}/pinpoint/campagnes/{cid}/canaux":{"get":{"operationId":"canaux_campagne_pinpoint","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Résumé des DEUX canaux (courriel + webhook), rendus SÉPARÉS, jamais fusionnés"},"404":{"description":"Campagne inconnue"}},"tags":["notifications"]}},"/v1/projects/{pid}/pinpoint/campagnes/{cid}/dupliquer":{"post":{"operationId":"dupliquer_campagne_pinpoint","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Copie en brouillon (stats à zéro)"}},"tags":["notifications"]}},"/v1/projects/{pid}/pinpoint/campagnes/{cid}/envoyer":{"post":{"operationId":"envoyer_campagne_pinpoint","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Envoi réel via le relais SMTP — un à un, stats et journal alimentés"}},"tags":["notifications"]}},"/v1/projects/{pid}/pinpoint/campagnes/{cid}/webhook":{"post":{"operationId":"envoyer_webhook_pinpoint","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnvoyerWebhookReq"}}},"required":true},"responses":{"200":{"description":"Variante WEBHOOK livrée : charge utile POSTée à l'URL pour CHAQUE destinataire, code HTTP MESURÉ par livraison — stats du canal séparées du courriel"}},"tags":["notifications"]}},"/v1/projects/{pid}/pinpoint/segments":{"post":{"operationId":"creer_segment_pinpoint","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewSegmentReq"}}},"required":true},"responses":{"201":{"description":"Segment créé (critère : tous|admin|dev|lecteur)"}},"tags":["notifications"]}},"/v1/projects/{pid}/pinpoint/segments-attributs":{"get":{"operationId":"lister_segments_attributs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Segments par prédicats (évalués à l'ENVOI — l'effectif n'est jamais deviné, figé après évaluation)"}},"tags":["notifications"]},"post":{"operationId":"creer_segment_attributs_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewSegmentAttributsReq"}}},"required":true},"responses":{"201":{"description":"Segment créé — le prédicat est réévalué À L'ENVOI sur les attributs frais des membres"},"400":{"description":"Prédicat structurellement invalide"}},"tags":["notifications"]}},"/v1/projects/{pid}/pinpoint/segments-attributs/{sid}":{"delete":{"operationId":"supprimer_segment_attributs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Segment retiré"},"404":{"description":"Segment inconnu"},"409":{"description":"Une campagne cible ce segment"}},"tags":["notifications"]}},"/v1/projects/{pid}/pinpoint/segments/{sid}":{"delete":{"operationId":"supprimer_segment_pinpoint","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Segment retiré (refusé si une campagne l'utilise)"}},"tags":["notifications"]}},"/v1/projects/{pid}/pipelines":{"get":{"operationId":"lister_pipelines","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pipelines CI/CD de l'org + dernière exécution"}},"tags":["gouvernance"]},"post":{"operationId":"creer_pipeline","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewPipelineReq"}}},"required":true},"responses":{"201":{"description":"Pipeline créé (source vérifiée à l'exécution, jamais à la création)"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/pipelines/executions/{eid}/retour-arriere":{"post":{"operationId":"retour_arriere_pipeline","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"eid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Retour arrière : la révision d'avant-déploiement reprend 100 % du trafic"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/pipelines/{plid}":{"delete":{"operationId":"supprimer_pipeline","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"plid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pipeline supprimé (l'historique des exécutions est conservé)"}},"tags":["gouvernance"]},"get":{"operationId":"detail_pipeline","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"plid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pipeline + historique des exécutions"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/pipelines/{plid}/executer":{"post":{"operationId":"executer_pipeline","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"plid","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Exécution synchrone : source vérifiée par HTTP, déploiement d'une révision, sonde éventuelle — journal complet"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/pipelines/{plid}/executions":{"get":{"operationId":"lister_executions_pipeline","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"plid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Historique des exécutions du pipeline"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/politiques":{"get":{"operationId":"lister_politiques","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Politiques de l'organisation"}},"tags":["general"]},"post":{"operationId":"creer_politique","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewPolitiqueReq"}}},"required":true},"responses":{"201":{"description":"Politique créée (bloquante par défaut, audit si bloquante=false)"}},"tags":["general"]}},"/v1/projects/{pid}/politiques/conformite":{"get":{"operationId":"audit_conformite","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Audit de conformité des ressources EXISTANTES contre les politiques"}},"tags":["general"]}},"/v1/projects/{pid}/politiques/{pol_id}":{"delete":{"operationId":"supprimer_politique","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"pol_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Politique supprimée"}},"tags":["general"]}},"/v1/projects/{pid}/portail/liens":{"get":{"operationId":"liens_portail_lire","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Liens curatés du portail (liste, vide par défaut)"}},"summary":"Liens curatés d'une organisation — lecture (GET, project.read).","tags":["public"]},"put":{"operationId":"liens_portail_ecrire","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiensReq"}}},"required":true},"responses":{"200":{"description":"Liste remplacée"},"400":{"description":"LIEN_INVALIDE — nom vide/long, URL non http(s), ou plus de 64 liens"}},"summary":"Liens curatés — écriture (PUT, project.write) : remplace la liste.\nLes URLs doivent être http(s), 64 liens max, noms de 64 caractères max.","tags":["public"]}},"/v1/projects/{pid}/portail/login":{"post":{"operationId":"login_portail","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginPortailReq"}}},"required":true},"responses":{"200":{"description":"Login RÉEL : mot de passe vérifié par l'IAM, rôle mesuré dans l'org (admin-org → admin, dev → dev, lecteur → lecteur), session de vue portail délivrée (jeton, TTL borné)"},"401":{"description":"IDENTIFIANTS_INVALIDES — email inconnu ou mot de passe incorrect (les mêmes mots pour les deux, rien n'est distinguable)"},"403":{"description":"HORS_ORGANISATION — compte connu mais pas membre de cette org"}},"tags":["general"]}},"/v1/projects/{pid}/portail/session":{"get":{"operationId":"lire_session_portail","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Jeton de la session à relire","in":"query","name":"jeton","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Validité de la session (expiration MESURÉE) — expirée ou inconnue : dit tel quel"}},"tags":["general"]},"post":{"operationId":"creer_session_portail_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreerSessionPortailReq"}}},"required":true},"responses":{"201":{"description":"Session de vue créée (rôle connu, TTL borné) — STRUCTURE SEULE : le câblage login/SSO reste à l'intégrateur, le jeton vient de lui"}},"tags":["general"]}},"/v1/projects/{pid}/portail/surcharge":{"get":{"operationId":"lire_surcharge_portail","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Surcharge de regroupement posée (ordre, renommages, fusions)"}},"tags":["general"]},"put":{"operationId":"poser_surcharge_portail","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SurchargeGroupes"}}},"required":true},"responses":{"200":{"description":"Surcharge posée — fusions, puis ordre, puis renommages, appliqués aux titres d'inventaire"}},"tags":["general"]}},"/v1/projects/{pid}/portail/theme":{"get":{"operationId":"lire_theme_portail","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Thème du portail (champs posés uniquement, le reste est la charte par défaut)"}},"tags":["general"]},"put":{"operationId":"poser_theme_portail","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThemePortail"}}},"required":true},"responses":{"200":{"description":"Thème posé — couleurs #RRGGBB, logo http(s)/data:image, titre"}},"tags":["general"]}},"/v1/projects/{pid}/portail/widgets":{"get":{"operationId":"widgets_portail","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Widgets statut/coût/GPU alimentés par les mesures réelles accessibles (status page, lignes de facture, CPU mesuré des groupes GPU) — sans mesure : sans_mesure, dit"}},"tags":["general"]}},"/v1/projects/{pid}/protection/cycle":{"post":{"operationId":"cycle_protection","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"Le cycle périodique : snapshots dus + purge + tests dus. C'est LUI qu'un\nscheduler déclenche en production (cron interne du lot observabilité).","tags":["securite"]}},"/v1/projects/{pid}/quota-requests":{"get":{"operationId":"lister_demandes_quota","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Demandes de quota de l'org (tous états)"}},"tags":["quotas"]},"post":{"operationId":"creer_demande_quota","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewDemandeQuotaReq"}}},"required":true},"responses":{"201":{"description":"Demande enregistrée — en attente d'un admin plateforme"}},"tags":["quotas"]}},"/v1/projects/{pid}/quota-requests/{id}/decision":{"post":{"operationId":"decider_demande_quota","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionReq"}}},"required":true},"responses":{"200":{"description":"Décision rendue (admin plateforme) — le quota s'applique immédiatement"}},"tags":["quotas"]}},"/v1/projects/{pid}/quotas":{"get":{"operationId":"lire_quotas","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Quotas de l'org + utilisation + pourcentages"}},"tags":["quotas"]},"put":{"operationId":"definir_quotas","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DefinitionQuotasReq"}}},"required":true},"responses":{"200":{"description":"Quotas DÉFINIS directement (superadmin) — effet immédiat"}},"tags":["quotas"]}},"/v1/projects/{pid}/rapport":{"get":{"operationId":"rapport_projet","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"Rapport consolidé (à programmer via un cron pour l'envoi périodique).","tags":["automatisation"]}},"/v1/projects/{pid}/rapports":{"get":{"operationId":"lister_rapports","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rapports définis"}},"tags":["general"]},"post":{"operationId":"creer_rapport","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRapportReq"}}},"required":true},"responses":{"201":{"description":"Rapport créé"}},"tags":["general"]}},"/v1/projects/{pid}/rapports/grafana/offre":{"get":{"operationId":"offre_grafana_rapports","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Version EXACTE vérifiée sur grafana.com (semver X.Y.Z — jamais latest, jamais une substitution)","in":"query","name":"version","required":true,"schema":{"type":"string"}},{"description":"Hôte public du Grafana (GF_SERVER_ROOT_URL, ex. bi.nantes.cirro)","in":"query","name":"hote","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"docker run EXACT du Grafana BI (ports liés à localhost, volume persistant, GF_SERVER_ROOT_URL) + la datasource du FerroQuery RÉEL du premier entrepôt actif de l'org (sinon DIT) + le dashboard des requêtes nommées RÉELLES du module rapports (machines, vms, couts, evenements — SQL dérivé des colonnes réelles) + la GARDE verifier_plan_grafana passée sur le plan rendu"},"400":{"description":"PLAN_GRAFANA_REFUSE — version floue/latest, hôte invalide"}},"tags":["general"]}},"/v1/projects/{pid}/rapports/pdf/{jid}/envoyer":{"post":{"operationId":"envoyer_pdf_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"jid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Notification partie par le relais SMTP réel, job marqué du résultat mesuré"},"409":{"description":"Aucune pièce rendue — le PDF vient du rendeur externe"}},"tags":["general"]}},"/v1/projects/{pid}/rapports/pdf/{jid}/piece":{"post":{"operationId":"inscrire_piece_pdf","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"jid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PiecePdfReq"}}},"required":true},"responses":{"200":{"description":"Pièce INSCRITE (métadonnées réelles) — état pret"}},"tags":["general"]}},"/v1/projects/{pid}/rapports/{rid}":{"delete":{"operationId":"supprimer_rapport","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rapport supprimé"}},"tags":["general"]},"get":{"operationId":"detail_rapport","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Définition + dernière exécution"}},"tags":["general"]}},"/v1/projects/{pid}/rapports/{rid}/csv":{"get":{"operationId":"rapport_csv","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Dernière exécution en CSV (409 si jamais exécuté)"}},"tags":["general"]}},"/v1/projects/{pid}/rapports/{rid}/executer":{"post":{"operationId":"executer_rapport","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rapport exécuté sur données réelles"}},"tags":["general"]}},"/v1/projects/{pid}/rapports/{rid}/pdf":{"get":{"operationId":"lister_jobs_pdf","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Jobs PDF du rapport — échéances dues (du_envoyer_pdf) dites honnêtement"}},"tags":["general"]},"post":{"operationId":"creer_job_pdf_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewJobPdfReq"}}},"required":true},"responses":{"201":{"description":"Job PDF créé (état planifie) — AUCUNE pièce tant que le rendeur externe n'a pas produit le fichier"}},"tags":["general"]}},"/v1/projects/{pid}/rapports/{rid}/seuil":{"delete":{"operationId":"retirer_seuil_rapport","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Seuil retiré"}},"tags":["general"]},"get":{"operationId":"lire_seuil_rapport","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Seuil posé + évaluation sur la DERNIÈRE exécution (notification si franchi)"},"404":{"description":"Aucun seuil posé"}},"tags":["general"]},"put":{"operationId":"poser_seuil_rapport","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeuilRapportReq"}}},"required":true},"responses":{"200":{"description":"Seuil posé — évalué à chaque exécution, sur la MESURE la plus récente de la colonne"}},"tags":["general"]}},"/v1/projects/{pid}/recherche":{"post":{"operationId":"chercher_recherche","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RechercheReq"}}},"required":true},"responses":{"200":{"description":"Résultats de recherche plein texte sur toutes les ressources"}},"tags":["general"]}},"/v1/projects/{pid}/recherche/indexer":{"post":{"operationId":"indexer_recherche","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Ressources indexées dans OpenSearch"}},"tags":["general"]}},"/v1/projects/{pid}/recommandations":{"get":{"operationId":"recommandations","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["automatisation"]}},"/v1/projects/{pid}/recommandations/economies":{"get":{"operationId":"economies_realisees","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Historique des économies confirmées + total cumulé au montant des annonces"}},"tags":["automatisation"]}},"/v1/projects/{pid}/recommandations/{rid}/confirmer":{"post":{"operationId":"confirmer_recommandation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"id de la recommandation (ex. vms-arretees)","in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmerRecoReq"}}},"required":true},"responses":{"201":{"description":"Économie enregistrée au montant annoncé — le compteur cumule"},"404":{"description":"Recommandation inconnue de l'inventaire courant"},"409":{"description":"Recommandation déjà comptée"}},"tags":["automatisation"]}},"/v1/projects/{pid}/recos/etendues":{"get":{"operationId":"recos_etendues_couts_ressources","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Recos étendues DÉTECTÉES sur les registres réels : snapshots de volumes (âge MESURÉ, « jamais restauré » = aucune restauration JOURNALISÉE — le registre ne porte pas ce champ, DIT) et apps parked (l'âge est borné par le dernier accès, la date de passage en parked n'est pas journalisée — DIT) ; les IP réservées n'ont AUCUN registre dans le produit à ce jour — rien détecté pour ce genre, rien inventé ; aucune reco ne naît confirmée"}},"tags":["couts"]}},"/v1/projects/{pid}/recos/menage":{"post":{"operationId":"menage_couts_ressources","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MenageReq"}}},"required":true},"responses":{"200":{"description":"Calendrier de ménage déroulé : seules les recos CONFIRMÉES sont retenues (les autres exclues, comptées), un lot de 10 par périodicité — jamais tout d'un coup ; le plan est RENDU, l'exécution appartient à l'intégrateur"},"400":{"description":"MENAGE_REFUSE — périodicité nulle, ou plan prétendant s'appliquer sans confirmation (rien ne s'applique sans elle)"}},"tags":["couts"]}},"/v1/projects/{pid}/registre":{"get":{"operationId":"etat_registre","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Dépôts + tags du registre d'images (zot) et disponibilité du scanner"}},"tags":["donnees"]},"post":{"operationId":"scanner_registre","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanReq"}}},"required":true},"responses":{"201":{"description":"Scan trivy exécuté : résumé par sévérité + top 20 CVE"},"503":{"description":"Registre ou trivy absent du nœud"}},"tags":["donnees"]}},"/v1/projects/{pid}/registre/scans":{"get":{"operationId":"historique_scans","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Historique des scans (50 derniers)"}},"tags":["donnees"]}},"/v1/projects/{pid}/regles":{"get":{"operationId":"lister_regles_routage","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règles de routage d'événements de l'org (secrets masqués)"}},"tags":["general"]},"post":{"operationId":"creer_regle_routage","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRegleReq"}}},"required":true},"responses":{"201":{"description":"Règle créée — secret HMAC rendu UNE seule fois"}},"tags":["general"]}},"/v1/projects/{pid}/regles/metriques":{"get":{"operationId":"metriques_regles","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Snapshot des compteurs MESURÉS par règle de routage, trié par déclenchements DÉCROISSANTS : livrés, échecs, taux d'échec en % arrondi à 1 décimale, dernière durée observée TELLE QUELLE (jamais une moyenne) ; les compteurs viennent du journal que tient l'intégration (noter_livraison à chaque livraison) — journal vide : rendu tel quel avec la note honnête, les déclenchements cumulés des règles restent dans GET …/regles"}},"tags":["general"]}},"/v1/projects/{pid}/regles/riche":{"post":{"operationId":"creer_regle_riche","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRegleRicheReq"}}},"required":true},"responses":{"201":{"description":"Règle créée avec son FILTRE RICHE — secret HMAC rendu UNE seule fois ; chaque événement réel est évalué par correspond_riche sur ses attributs"},"400":{"description":"REGLE_INVALIDE — mêmes gardes que les règles simples"},"409":{"description":"DEJA_PRIS — une règle porte déjà ce nom"}},"tags":["general"]}},"/v1/projects/{pid}/regles/test":{"post":{"operationId":"tester_regles_routage","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestRegleApiReq"}}},"required":true},"responses":{"200":{"description":"Simulation : règles actives qui attraperaient cet événement — filtres simples par (genre, titre), filtres riches par (genre, attributs)"}},"tags":["general"]}},"/v1/projects/{pid}/regles/{rid}":{"delete":{"operationId":"supprimer_regle_routage","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règle supprimée"}},"tags":["general"]}},"/v1/projects/{pid}/relations":{"get":{"operationId":"lister_relations","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["systeme"]}},"/v1/projects/{pid}/reservations":{"get":{"operationId":"lister_reservations","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Réservations de l'org + couverture réelle (VMs actives, économies)"}},"tags":["gouvernance"]},"post":{"operationId":"creer_reservation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewReservationReq"}}},"required":true},"responses":{"201":{"description":"Réservation créée — engagement de budget, réduction appliquée aux VMs actives couvertes"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/reservations/alertes-couverture":{"get":{"operationId":"alertes_couverture_reservations","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Réservations actives dont la couverture réelle est sous le seuil — € non réalisés chiffrés"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/reservations/conseil":{"get":{"operationId":"conseil_reservations","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Quoi réserver (plancher observé), quoi libérer (surplus au-delà du maximum), économies chiffrées — gabarits sans mesure nommés"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/reservations/{rid}":{"delete":{"operationId":"resilier_reservation","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Réservation résiliée (l'engagement est cassé : plus de réduction, conservée pour l'historique)"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/reservations/{rid}/echanger":{"post":{"operationId":"echanger_reservation_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Réservation (res-…)","in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EchangerReservationReq"}}},"required":true},"responses":{"200":{"description":"Échange SANS recréation : l'id, l'org, la durée et la fenêtre d'engagement (cree_le / expire_le) restent CEUX de la réservation d'origine — même id, pas de nouvelle réservation ; la trace d'échange (avant → après, date) est rendue et l'audit tient dans une phrase"},"400":{"description":"ECHANGE_REFUSE — gabarit inconnu du catalogue (cité), delta sous la quantité courante, quantité finale au-delà du plafond"},"404":{"description":"Réservation inconnue de l'org"}},"tags":["couts"]}},"/v1/projects/{pid}/routes":{"get":{"operationId":"lister_routes","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["reseau"]},"post":{"operationId":"creer_route_http","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRouteReq"}}},"required":true},"responses":{"201":{"description":"Créé"}},"tags":["reseau"]}},"/v1/projects/{pid}/routes/{rid}":{"delete":{"operationId":"supprimer_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["reseau"]}},"/v1/projects/{pid}/s3/cles":{"get":{"operationId":"lister_cles_s3","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClesS3Liste"}}},"description":"Clés d'accès S3 de l'org — JAMAIS les secrets (rendus une seule fois à la création)"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Jeton absent/invalide"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"HORS_ORGANISATION / PERMISSION_REFUSEE — project.read requis"}},"tags":["donnees"]},"post":{"operationId":"creer_cle_s3","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewCleS3Req"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleS3Cree"}}},"description":"Clé créée — secret rendu UNE SEULE FOIS dans cette réponse"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Droits invalides (action inconnue, bucket manquant/mal nommé) ou NOM_INVALIDE"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Jeton absent/invalide"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"HORS_ORGANISATION / PERMISSION_REFUSEE — project.write requis"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Nom de clé déjà pris"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"La passerelle a refusé l'identité"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Passerelle S3 non active en mode registre seul"}},"tags":["donnees"]}},"/v1/projects/{pid}/s3/cles/{id}":{"delete":{"operationId":"supprimer_cle_s3","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Id de la clé (s3k-…)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleS3Supprimee"}}},"description":"Clé révoquée — l'identité est retirée de la passerelle (passerelle d'abord : un refus laisse la clé valable)"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Jeton absent/invalide"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"HORS_ORGANISATION / PERMISSION_REFUSEE — project.write requis"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Clé inconnue de cette organisation"}},"tags":["donnees"]}},"/v1/projects/{pid}/s3/sse":{"get":{"operationId":"contrat_sse_objets","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Contrat du chiffrement au repos SSE des objets : AES-256-GCM (clé de données ALÉATOIRE par objet, enveloppée par la KEK de l'org dérivée HKDF-SHA256 — une org ne déchiffre pas les objets d'une autre), AAD liant le chiffré à sa clé, API BORNÉE à 8 Mio (les gros objets passent par le flux existant, DIT au refus) ; la clé racine n'est fournie par AUCUN keystore d'org à ce jour — elle est exigée dans le corps des POST (cle_racine_base64, 32 octets), refus honnête sinon"}},"tags":["stockage"]}},"/v1/projects/{pid}/s3/sse/chiffrer":{"post":{"operationId":"chiffrer_sse_objets","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChiffrerSseReq"}}},"required":true},"responses":{"200":{"description":"Objet chiffré SSE : clé de données ALÉATOIRE par objet ENVELOPPÉE par la KEK de l'org (jamais la clé nue), contenu AES-256-GCM (tag inclus), nonce, algo — plus les en-têtes SSE équivalents SSE-KMS (x-amz-server-side-encryption: AES256 + id de clé qui NOMME l'org) ; rien n'est posé sur la passerelle ici"},"400":{"description":"CLE_RACINE_REQUISE / CLE_RACINE_INVALIDE / CHIFFREMENT_REFUSE — refus NOMMÉ : clé racine absente (aucun keystore d'org à ce jour — DIT), base64 illisible, org/clé vide, contenu > 8 Mio (les gros objets passent par le flux existant)"}},"tags":["stockage"]}},"/v1/projects/{pid}/s3/sse/dechiffrer":{"post":{"operationId":"dechiffrer_sse_objets","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DechiffrerSseReq"}}},"required":true},"responses":{"200":{"description":"Contenu en clair (base64) : erreurs DISTINGUÉES par le noyau — l'enveloppe ne s'ouvre pas = « org » (KEK dérivée par org ou clé racine erronée), le contenu ne s'authentifie pas = « intégrité » (bit retourné, nonce altéré) ; jamais d'octet deviné, aucun rendu partiel"},"400":{"description":"CLE_RACINE_REQUISE / CLE_RACINE_INVALIDE / DECHIFFREMENT_REFUSE — refus NOMMÉ : clé racine absente, base64 illisibles, algo inconnu, org ou intégrité en cause (citées)"}},"tags":["stockage"]}},"/v1/projects/{pid}/s3/{bucket}/multipart":{"post":{"operationId":"decrire_multipart_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bucket","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MultipartReq"}}},"required":true},"responses":{"200":{"description":"Descripteur assemblé : ETag de chaque partie = empreinte SHA-256 de son contenu (SIMULÉ — le vrai ETag multipart S3 est un MD5 composé par la passerelle, DIT tel), taille totale MESURÉE, ETag final en forme hash-N"},"400":{"description":"MULTIPART_REFUSE — aucune partie, numéro nul, doublon ou TROU dans la numérotation (S3 assemble 1..N consécutifs — le trou est cité), base64 illisible"}},"tags":["stockage"]}},"/v1/projects/{pid}/s3/{bucket}/objets":{"delete":{"operationId":"supprimer_objet_s3","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"NOM du bucket (pas son id) — ex. « logs-bruts »","in":"path","name":"bucket","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupprimerObjetS3Req"}}},"required":true},"responses":{"200":{"description":"Objet supprimé (DELETE nu sur la passerelle) — compteurs du registre rafraîchis par re-comptage live"},"400":{"description":"Clé manquante ou invalide"},"404":{"description":"Bucket introuvable dans cette organisation"},"502":{"description":"La passerelle S3 a refusé la suppression"},"503":{"description":"Passerelle S3 injoignable, ou mode registre seul — rien n'est supprimé alors"}},"tags":["donnees"]},"get":{"operationId":"lister_objets_s3","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"NOM du bucket (pas son id) — ex. « logs-bruts »","in":"path","name":"bucket","required":true,"schema":{"type":"string"}},{"description":"Jeton de continuation ListObjectsV2 pour poursuivre un listage tronqué","in":"query","name":"jeton","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Objets du bucket — liste TOUT le bucket (clés avec `/` incluses, ce sont des chaînes opaques) ; tronque=true si le plafond de pagination est atteint, jeton_suivant pour continuer"},"404":{"description":"Bucket introuvable dans cette organisation"},"502":{"description":"La passerelle S3 a refusé le listage"},"503":{"description":"Passerelle S3 injoignable, ou mode registre seul (CIRRO_WORKLOADS_REELS=0) — jamais de liste inventée"}},"tags":["donnees"]}},"/v1/projects/{pid}/s3/{bucket}/objets/contenu":{"get":{"operationId":"contenu_objet_s3","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"NOM du bucket (pas son id) — ex. « logs-bruts »","in":"path","name":"bucket","required":true,"schema":{"type":"string"}},{"description":"Clé de l'objet — chaîne opaque, les `/` qu'elle contient font partie de la clé (percent-encodés dans l'URL par le client)","in":"query","name":"cle","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Contenu BRUT de l'objet — Content-Type: application/octet-stream, Content-Disposition: attachment (nom de fichier = dernier segment de la clé)"},"400":{"description":"Clé manquante ou invalide"},"404":{"description":"Bucket ou objet introuvable"},"413":{"description":"Objet plus lourd que la limite de téléchargement API (20 Mo) — honnête, jamais tronqué"},"502":{"description":"La passerelle S3 a refusé le téléchargement"},"503":{"description":"Passerelle S3 injoignable, ou mode registre seul — aucun contenu inventé"}},"tags":["donnees"]}},"/v1/projects/{pid}/s3/{bucket}/objets/verrou":{"delete":{"operationId":"retirer_verrou_objet","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"NOM du bucket","in":"path","name":"bucket","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoserVerrouReq"}}},"required":true},"responses":{"200":{"description":"Verrou retiré (acte de conformité audité — l'objet redevient supprimable)"},"404":{"description":"Aucun verrou sur cette clé"}},"tags":["donnees"]},"get":{"operationId":"lire_verrou_objet","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"NOM du bucket","in":"path","name":"bucket","required":true,"schema":{"type":"string"}},{"description":"Clé de l'objet verrouillé","in":"query","name":"cle","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Verrou + verdict de suppression à l'instant"},"404":{"description":"Aucun verrou sur cette clé"}},"tags":["donnees"]},"put":{"operationId":"poser_verrou_objet","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"NOM du bucket","in":"path","name":"bucket","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoserVerrouReq"}}},"required":true},"responses":{"200":{"description":"Verrou posé (mode conformité — le DELETE de l'objet sera refusé jusqu'à l'échéance)"},"400":{"description":"Échéance passée ou illisible"}},"tags":["donnees"]}},"/v1/projects/{pid}/s3/{bucket}/tiering":{"get":{"operationId":"lire_tiering_bucket","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bucket","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Politique de tiering du bucket — null s'il n'y en a pas (dit tel : sans règle, rien n'est jamais déplacé)"}},"tags":["stockage"]},"put":{"operationId":"poser_tiering_bucket","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bucket","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolitiqueTieringReq"}}},"required":true},"responses":{"200":{"description":"Politique posée : la transition se décidera sur l'âge d'ACCÈS mesuré — jamais sur l'âge de création"},"400":{"description":"TIERING_INVALIDE — âge nul ou classe cible inconnue, raisons nommées"}},"tags":["stockage"]}},"/v1/projects/{pid}/s3/{bucket}/tiering/evaluer":{"post":{"operationId":"evaluer_tiering_bucket","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bucket","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvaluerTieringReq"}}},"required":true},"responses":{"200":{"description":"Une décision PAR objet, datée de maintenant : sans règle pour ce bucket → conserve (jamais déplacé sans règle) ; âge d'accès non mesuré ou date illisible → conserve (l'âge ne peut pas être PROUVÉ) ; âge mesuré ≥ seuil → transition, la raison cite les nombres"},"400":{"description":"EVALUATION_INVALIDE — date illisible dans une demande explicite"}},"tags":["stockage"]}},"/v1/projects/{pid}/s3/{bucket}/versions":{"get":{"operationId":"lister_versions_s3","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bucket","required":true,"schema":{"type":"string"}},{"description":"Filtre par clé d'objet","in":"query","name":"cle","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Versions déclarées du bucket, triées par date croissante puis id (la plus ancienne d'abord) — la courante reste marquée TELLE QUE fournie, ce module ne déduit jamais laquelle est courante"}},"tags":["stockage"]},"post":{"operationId":"declarer_versions_s3","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bucket","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersionsReq"}}},"required":true},"responses":{"200":{"description":"Versions déclarées (remplacées) — elles viennent du stockage réel, mesurées par l'appelant"}},"tags":["stockage"]}},"/v1/projects/{pid}/s3/{bucket}/versions/restaurer":{"post":{"operationId":"restaurer_version_s3","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"bucket","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestaurerVersionReq"}}},"required":true},"responses":{"200":{"description":"Restauration = COPIE PLANIFIÉE (statut « planifiee », jamais exécutée par ce module) — la version doit appartenir à la clé demandée, le mélange est refusé en citant les deux"},"400":{"description":"RESTAURATION_REFUSEE — clé/id vides, version introuvable ou appartenant à une autre clé"},"404":{"description":"Aucune version déclarée sur ce bucket"}},"tags":["stockage"]}},"/v1/projects/{pid}/scim/actions":{"get":{"operationId":"actions_scim","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Journal des dernières actions SCIM (100 max, compteur mesuré)"}},"tags":["securite"]}},"/v1/projects/{pid}/scim/apply":{"post":{"operationId":"appliquer_scim","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Diff appliqué : les RETRAITS sont réels (membres retirés de l'org) ; créations/modifications exigent un utilisateur inscrit — journalisées en attente, jamais exécutées en silence. Preuve d'idempotence : le re-jeu du diff sur le nouvel état est RENDU"},"404":{"description":"Aucun annuaire mémorisé — POST …/scim/diff d'abord"}},"tags":["securite"]}},"/v1/projects/{pid}/scim/diff":{"post":{"operationId":"diffuser_scim","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScimDiffReq"}}},"required":true},"responses":{"200":{"description":"Diff SCIM : à créer/retirer/modifier, conflits nommés exclus de toute action"}},"tags":["iam"]}},"/v1/projects/{pid}/secrets":{"get":{"operationId":"lister_secrets","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretsListe"}}},"description":"Secrets de l'org — valeurs JAMAIS rendues en liste (EX-SEC-002)"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Jeton absent/invalide"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"HORS_ORGANISATION / PERMISSION_REFUSEE — project.read requis"}},"tags":["donnees"]},"post":{"operationId":"creer_secret","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewSecretReq"}}},"required":true},"responses":{"201":{"description":"Créé"}},"tags":["donnees"]}},"/v1/projects/{pid}/secrets/{sid}":{"delete":{"operationId":"supprimer_secret","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["donnees"]},"get":{"operationId":"detail_secret","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail du secret — valeur MASQUÉE (\"***\") : la valeur en clair n'est rendue qu'UNE SEULE FOIS, à la création (201)"}},"tags":["donnees"]},"patch":{"operationId":"maj_secret","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewSecretReq"}}},"required":true},"responses":{"200":{"description":"OK"}},"tags":["donnees"]}},"/v1/projects/{pid}/secrets/{sid}/rotation":{"put":{"operationId":"maj_rotation_secret","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MajRotationReq"}}},"required":true},"responses":{"200":{"description":"Politique de rotation posée (jours 0 = désactivée) — ATTENTION : la rotation remplace la valeur par une valeur aléatoire (secrets générés par machine)"}},"tags":["donnees"]}},"/v1/projects/{pid}/secrets/{sid}/rotation/executer":{"post":{"operationId":"executer_rotation_secret","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rotation exécutée maintenant — nouvelle version, valeur aléatoire générée"}},"tags":["donnees"]}},"/v1/projects/{pid}/securite/hub":{"get":{"operationId":"hub_securite","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Posture de sécurité consolidée : score, conformité, anomalies, chaîne d'audit"}},"summary":"cirroHub sécurité (03/09 — équivalent AWS Security Hub / GCP Security\nCommand Center) : UNE réponse qui regroupe toute la posture — score 9\ncontrôles, conformité aux politiques, anomalies ouvertes (H1-H6) et\nintégrité de la chaîne d'audit.","tags":["securite"]}},"/v1/projects/{pid}/security-groups":{"get":{"operationId":"lister_security_groups","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Security groups de l'org — un groupe PAR machine (allow-only, règles d'autorisation)"}},"tags":["reseau"]}},"/v1/projects/{pid}/security-groups/{machine}":{"delete":{"operationId":"supprimer_security_group","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"machine","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Groupe et table d'état supprimés"},"404":{"description":"Machine sans security group"}},"tags":["reseau"]},"put":{"operationId":"poser_security_group","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Id IPAM de la machine — un groupe PAR machine","in":"path","name":"machine","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupeSecuriteReq"}}},"required":true},"responses":{"200":{"description":"Groupe posé (règles validées : genres, bornes de ports, CIDR) — la table d'état repart vide"},"400":{"description":"REGLE_SG_INVALIDE — chaque manquement nommé"}},"tags":["reseau"]}},"/v1/projects/{pid}/security-groups/{machine}/flow":{"post":{"operationId":"decider_flow_security_group","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"machine","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowObserveeReq"}}},"required":true},"responses":{"200":{"description":"Verdict sur la flow OBSERVÉE : autorisée PAR L'ÉTAT (retour d'une conversation ouverte) ou PAR UNE RÈGLE (la conversation entre à l'état), sinon refusée — purges comptées"},"404":{"description":"Machine sans security group"}},"tags":["reseau"]}},"/v1/projects/{pid}/security-score":{"get":{"operationId":"score_securite","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["securite"]}},"/v1/projects/{pid}/sensible":{"get":{"operationId":"lister_scans_sensibles","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Historique des balayages de sensibilité"}},"tags":["securite"]},"post":{"operationId":"analyser_sensible","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyserReq"}}},"required":true},"responses":{"201":{"description":"Balayage réel exécuté — findings masqués, score pondéré"}},"tags":["securite"]}},"/v1/projects/{pid}/sensible/classifier":{"post":{"operationId":"classifier_sensible","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClassifierReq"}}},"required":true},"responses":{"200":{"description":"Classification PII lexicale déterministe — étiquette, score, caractéristiques qui ont pesé ; le texte n'est jamais persisté"}},"tags":["securite"]}},"/v1/projects/{pid}/sensible/colonnes":{"post":{"operationId":"balayer_colonnes_sensible","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalayerColonnesReq"}}},"required":true},"responses":{"201":{"description":"Balayage des colonnes FerroDB par métadonnées fournies — genre, score, échantillons masqués (aucune connexion DB directe)"}},"tags":["securite"]}},"/v1/projects/{pid}/sensible/ocr":{"post":{"operationId":"analyser_ocr_sensible","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OcrReq"}}},"required":true},"responses":{"200":{"description":"Détecteurs appliqués au texte OCR FOURNI par l'appelant — aucun moteur OCR embarqué ni simulé"}},"tags":["securite"]}},"/v1/projects/{pid}/sensible/rgpd":{"get":{"operationId":"rapport_rgpd_sensible","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"json (défaut) ou csv — cartographie, localisations, recommandations","in":"query","name":"format","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rapport RGPD exportable — cartographie par genre, localisations fichier/colonne, recommandations chiffrer/purger/anonymiser, score global"}},"tags":["securite"]}},"/v1/projects/{pid}/sensible/{rid}":{"delete":{"operationId":"supprimer_scan_sensible","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rapport de balayage supprimé"}},"tags":["securite"]},"get":{"operationId":"detail_scan_sensible","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'un balayage — findings masqués par fichier"}},"tags":["securite"]}},"/v1/projects/{pid}/serveurs":{"get":{"operationId":"lister_serveurs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Serveurs externes déclarés (credentials masqués)"}},"tags":["materiel"]},"post":{"operationId":"creer_serveur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewServeurReq"}}},"required":true},"responses":{"201":{"description":"Serveur déclaré + test SSH effectué (Mi1 : 201, création)"},"400":{"description":"UTILISATEUR_ROOT_REFUSE — un serveur-pod refuse root (Mo3)"}},"tags":["materiel"]}},"/v1/projects/{pid}/serveurs/decouvrir":{"post":{"operationId":"decouvrir_serveurs","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecouvrirReq"}}},"required":true},"responses":{"200":{"description":"Serveurs SSH trouvés sur le /24 (scan parallèle)"}},"tags":["materiel"]}},"/v1/projects/{pid}/serveurs/{sid}":{"delete":{"operationId":"supprimer_serveur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Serveur retiré du registre"}},"tags":["materiel"]}},"/v1/projects/{pid}/serveurs/{sid}/executer":{"post":{"operationId":"executer_sur_serveur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuterCommandeServeurReq"}}},"required":true},"responses":{"200":{"description":"Commande exécutée via SSH — corps : {\"commande\": str, \"stdout\": str, \"stderr\": str, \"code\": int}"},"404":{"description":"Serveur introuvable"},"422":{"description":"Corps invalide — le champ requis est `commande` (pas `sql`)"}},"tags":["materiel"]}},"/v1/projects/{pid}/serveurs/{sid}/test":{"post":{"operationId":"tester_serveur","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Test SSH (TCP + bannière) re-exécuté"}},"tags":["materiel"]}},"/v1/projects/{pid}/showback":{"get":{"operationId":"showback_org","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Coûts par service pour l'organisation"}},"tags":["systeme"]}},"/v1/projects/{pid}/status/abonnements":{"get":{"operationId":"lister_abonnements_status","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Abonnements aux changements de la status page"}},"tags":["systeme"]},"post":{"operationId":"creer_abonnement_status","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbonnementStatusReq"}}},"required":true},"responses":{"201":{"description":"Abonnement créé"},"400":{"description":"Genre inconnu ou URL non http(s)"}},"tags":["systeme"]}},"/v1/projects/{pid}/status/abonnements/{sid}":{"delete":{"operationId":"supprimer_abonnement_status","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Abonnement retiré"},"404":{"description":"Abonnement inconnu"}},"tags":["systeme"]}},"/v1/projects/{pid}/status/maintenances":{"get":{"operationId":"lister_maintenances_status","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Maintenances planifiées : bandeau actif, à venir, passées"}},"tags":["systeme"]},"post":{"operationId":"creer_maintenance_status","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MaintenanceReq"}}},"required":true},"responses":{"201":{"description":"Maintenance planifiée — le bandeau public ne l'affiche que pendant la plage"},"400":{"description":"Titre/dates invalides (fin après début exigée)"}},"tags":["systeme"]}},"/v1/projects/{pid}/status/maintenances/{mid}":{"delete":{"operationId":"supprimer_maintenance_status","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"mid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Maintenance annulée"},"404":{"description":"Maintenance inconnue"}},"tags":["systeme"]}},"/v1/projects/{pid}/status/post-mortems":{"get":{"operationId":"lister_post_mortems_status","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Post-mortems publiés (relus par la page publique des incidents)"}},"tags":["systeme"]},"post":{"operationId":"publier_post_mortem_status","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostMortemReq"}}},"required":true},"responses":{"201":{"description":"Post-mortem publié — la page publique de l'incident le relit"},"400":{"description":"Champs hors bornes (nommé)"},"404":{"description":"Incident inconnu des anomalies de l'org"}},"tags":["systeme"]}},"/v1/projects/{pid}/support":{"get":{"operationId":"lire_support","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plan de support + tickets avec SLA évalués (basic/argent/or, sévérités 1-4)"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/support/plan":{"post":{"operationId":"changer_plan_support","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangerPlanReq"}}},"required":true},"responses":{"200":{"description":"Plan changé (l'historique conserve les précédents)"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/support/tickets":{"post":{"operationId":"creer_ticket","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewTicketReq"}}},"required":true},"responses":{"201":{"description":"Ticket ouvert (SLA évalué sur horodatages réels)"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/support/tickets/{tid}":{"get":{"operationId":"detail_ticket","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Ticket + SLA (échéance, retard, respect)"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/support/tickets/{tid}/fermer":{"post":{"operationId":"fermer_ticket","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FermerReq"}}},"required":true},"responses":{"200":{"description":"Fermé sans résolution (raison conservée)"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/support/tickets/{tid}/repondre":{"post":{"operationId":"repondre_ticket","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Première réponse enregistrée → en_cours"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/support/tickets/{tid}/resoudre":{"post":{"operationId":"resoudre_ticket","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Résolu — délais et SLA tranchés"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/tags":{"get":{"operationId":"lire_tags","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"query","name":"ressource","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tags d'une ressource, ou inventaire complet si sans query"}},"tags":["general"]},"post":{"operationId":"poser_tag","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoserTagReq"}}},"required":true},"responses":{"201":{"description":"Tag posé sur la ressource"}},"tags":["general"]}},"/v1/projects/{pid}/tags/captures":{"get":{"operationId":"lister_captures_tags","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Captures conservées (résumés — dates et comptages)"}},"tags":["general"]},"post":{"operationId":"capturer_tags","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Capture prise — comparables par /tags/captures/diff"}},"tags":["general"]}},"/v1/projects/{pid}/tags/captures/diff":{"post":{"operationId":"diff_captures_tags","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiffCapturesReq"}}},"required":true},"responses":{"200":{"description":"Changements EXACTS entre les deux captures (créations, suppressions, ajouts, modifications, retraits)"},"404":{"description":"Capture inconnue"}},"tags":["general"]}},"/v1/projects/{pid}/tags/chercher":{"post":{"operationId":"chercher_par_tag","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChercherTagsReq"}}},"required":true},"responses":{"200":{"description":"Ressources dont un tag correspond au filtre (cle ou cle=valeur)"}},"tags":["general"]}},"/v1/projects/{pid}/tags/conformite":{"get":{"operationId":"conformite_tags","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"clés exigées séparées par des virgules (défaut : env,projet)","in":"query","name":"cles","required":false,"schema":{"type":"string"}},{"description":"csv : export CSV du rapport","in":"query","name":"format","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Scores de conformité par projet + écarts nommés (JSON ou CSV)"}},"summary":"Rapport de conformité des tags (FLUX W-10) : chaque ressource\ntaggable est notée sur les clés EXIGÉES (`?cles=env,projet`) — le\ngroupement « projet » est la valeur du tag `projet` (mesurée), les\nressources sans ce tag forment le groupe « sans-projet ». Écarts\nnommés (quelle ressource, quelle clé), export CSV via `?format=csv`.","tags":["general"]}},"/v1/projects/{pid}/tags/politique":{"get":{"operationId":"lire_politique_tags","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Clés exigées (vide = aucune exigence)"}},"tags":["general"]},"put":{"operationId":"poser_politique_tags","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolitiqueTagsReq"}}},"required":true},"responses":{"200":{"description":"Politique posée"},"400":{"description":"Clé impossible à poser, doublon, ou plus de 10 clés"}},"tags":["general"]}},"/v1/projects/{pid}/tags/retirer":{"post":{"operationId":"retirer_tag","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetirerTagReq"}}},"required":true},"responses":{"200":{"description":"Tag retiré"}},"tags":["general"]}},"/v1/projects/{pid}/tags/suggestions":{"get":{"operationId":"suggestions_tags","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"nom de la ressource à suggérer (ex. facture-api-prod)","in":"query","name":"nom","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tags proposés — projet puis env, rien inventé"}},"summary":"Suggestions de tags (FLUX W-10) : règles déterministes depuis le nom\n(env normalisé, premier segment non-environnement comme projet) —\nelles PROPOSENT, la pose reste un appel explicite.","tags":["general"]}},"/v1/projects/{pid}/topics":{"get":{"operationId":"lister_topics","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Topics de l'organisation (secrets masqués)"}},"tags":["general"]},"post":{"operationId":"creer_topic","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewTopicReq"}}},"required":true},"responses":{"201":{"description":"Topic créé — secret HMAC rendu UNE seule fois"}},"tags":["general"]}},"/v1/projects/{pid}/topics/pont/registre":{"get":{"operationId":"registre_pont_sujets","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Registre de sujets compatible Kafka (JSON, partitions=1, réplication=1, nature du pont DITE) — prêt à servir aux consommateurs"}},"tags":["general"]}},"/v1/projects/{pid}/topics/{tid}":{"delete":{"operationId":"supprimer_topic","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Topic supprimé"}},"tags":["general"]}},"/v1/projects/{pid}/topics/{tid}/abonnements":{"post":{"operationId":"creer_abonnement","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewAbonnementReq"}}},"required":true},"responses":{"201":{"description":"Abonnement ajouté au topic"}},"tags":["general"]}},"/v1/projects/{pid}/topics/{tid}/abonnements/{aid}":{"delete":{"operationId":"supprimer_abonnement","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Abonnement retiré"}},"tags":["general"]}},"/v1/projects/{pid}/topics/{tid}/abonnements/{aid}/trous":{"get":{"operationId":"trous_abonnement_topic","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}},{"in":"path","name":"aid","required":true,"schema":{"type":"string"}},{"description":"Séquences REÇUES par l'abonné, séparées par des virgules (ex. 1,2,5,6)","in":"query","name":"recues","required":true,"schema":{"type":"string"}},{"description":"Ne chercher les trous qu'au-delà de cette séquence (0 = depuis la première)","in":"query","name":"apres","required":true,"schema":{"format":"int64","minimum":0,"type":"integer"}}],"responses":{"200":{"description":"Trous NOMMÉS (plages de séquences manquantes, bornes comprises) — désordre et doublons tolérés, rien de reçu = rien d'affirmé"},"400":{"description":"SEQUENCES_ILLISIBLES — la liste ?recues= doit être des entiers"},"404":{"description":"Topic ou abonnement inconnu"}},"tags":["general"]}},"/v1/projects/{pid}/topics/{tid}/ordre-global":{"get":{"operationId":"lire_ordre_global_topic","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"État de l'ordre global du topic (désactivé par défaut : aucune séquence inventée)"},"404":{"description":"Topic inconnu"}},"tags":["general"]},"put":{"operationId":"poser_ordre_global_topic","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrdreGlobalReq"}}},"required":true},"responses":{"200":{"description":"Ordre global activé/désactivé — la séquence repart de 1 à l'activation (jamais réutilisée ensuite)"},"404":{"description":"Topic inconnu"}},"tags":["general"]}},"/v1/projects/{pid}/topics/{tid}/pont/consommer":{"post":{"operationId":"consommer_pont_sujet","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsommerPontKafkaReq"}}},"required":true},"responses":{"200":{"description":"Messages du journal du pont APRÈS le curseur, dans l'ordre des offsets"},"404":{"description":"Topic inconnu"}},"tags":["general"]}},"/v1/projects/{pid}/topics/{tid}/pont/traduire":{"post":{"operationId":"traduire_pont_sujet","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TraduirePontReq"}}},"required":true},"responses":{"200":{"description":"Publication traduite en message consommable du pont (clé=sujet, valeur INTACTE, offset=séquence) — le message traduit entre au journal du pont"},"400":{"description":"TRADUCTION_REFUSEE — ordre global désactivé (aucun offset), sujet vide ou timestamp non mesuré"},"404":{"description":"Topic inconnu"}},"tags":["general"]}},"/v1/projects/{pid}/topics/{tid}/publier":{"post":{"operationId":"publier_sur_topic","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublierReq"}}},"required":true},"responses":{"200":{"description":"Publication distribuée : livraisons et échecs comptés"}},"tags":["general"]}},"/v1/projects/{pid}/topics/{tid}/routage":{"get":{"operationId":"lire_routage_topic","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Règle du topic (défaut : seule la sévérité 1 pousse)"}},"tags":["general"]},"put":{"operationId":"poser_routage_topic","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegleRoutageReq"}}},"required":true},"responses":{"200":{"description":"Règle posée — appliquée à CHAQUE publication suivante"}},"tags":["general"]}},"/v1/projects/{pid}/traces":{"get":{"operationId":"lister_traces","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Traces mesurées de l'org (24 h, cap 500) + agrégats p50/p95"}},"tags":["systeme"]}},"/v1/projects/{pid}/traces/sampling":{"get":{"operationId":"lire_sampling_traces","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Politique en vigueur + compteurs mesurés (vues, gardées, taux)"}},"summary":"Politique de sampling des traces de l'org (FLUX W-13) : appliquée\npar le middleware à CHAQUE requête — le taux rendu est MESURÉ\n(gardées / vues depuis la dernière pose de politique).","tags":["systeme"]},"put":{"operationId":"poser_sampling_traces","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"Politique posée — le middleware l'applique à partir de la requête suivante"}},"tags":["systeme"]}},"/v1/projects/{pid}/traces/{tid}":{"get":{"operationId":"detail_trace","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'une trace (spans inclus)"},"404":{"description":"Trace inconnue ou hors rétention"}},"tags":["systeme"]}},"/v1/projects/{pid}/transcriptions":{"get":{"operationId":"lister_transcriptions","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Historique des transcriptions + état honnête du moteur"}},"tags":["general"]},"post":{"operationId":"transcrire_audio","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscrireReq"}}},"required":true},"responses":{"201":{"description":"Transcription réelle locale — texte + langue détectée par whisper.cpp"}},"tags":["general"]}},"/v1/projects/{pid}/transcriptions/{tid}":{"delete":{"operationId":"supprimer_transcription","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Transcription, WAV et rapport supprimés"}},"tags":["general"]},"get":{"operationId":"detail_transcription","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'une transcription (texte, langue, durées mesurées)"}},"tags":["general"]}},"/v1/projects/{pid}/transcriptions/{tid}/audio":{"get":{"operationId":"audio_transcription","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"tid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Le fichier WAV source (audio/wav)"}},"tags":["general"]}},"/v1/projects/{pid}/usecases/{slug}/instancier":{"post":{"operationId":"instancier_usecase","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstancierUcReq"}}},"required":true},"responses":{"200":{"description":"Étapes exécutées (instances) + manuelles à copier — les noms créés portent le préfixe demandé (champ prefixe en écho) ; 400 PREFIXE_INVALIDE/CORPS_INVALIDE si le corps ne respecte pas le format"}},"tags":["donnees"]}},"/v1/projects/{pid}/usecases/{slug}/parametres":{"get":{"operationId":"parametres_usecase","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Déclaration des paramètres {{clé}} du use case (genre, valeurs admises, défaut — le défaut restitue le texte d'origine)"}},"tags":["general"]}},"/v1/projects/{pid}/usecases/{slug}/plan":{"post":{"operationId":"plan_usecase","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanUsecaseReq"}}},"required":true},"responses":{"200":{"description":"Plan avec les {{clés}} substituées (valeurs validées par genre — un entier malformé est refusé)"},"400":{"description":"Valeur invalide pour un paramètre"}},"tags":["general"]}},"/v1/projects/{pid}/usecases/{slug}/variantes":{"get":{"operationId":"variantes_usecase","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Variantes par taille de site (solo | bi-site | gpu) — plans distincts, différences CHIFFRÉES dérivées des tarifs réels"}},"tags":["general"]}},"/v1/projects/{pid}/video":{"get":{"operationId":"lire_video","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Flux vidéo RTSP (segments ffmpeg réels, HLS, rétention)"}},"tags":["donnees"]}},"/v1/projects/{pid}/video/flux":{"post":{"operationId":"creer_flux_video","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewFluxReq"}}},"required":true},"responses":{"201":{"description":"Flux créé (veille) — démarrage ffmpeg à la demande"}},"tags":["donnees"]}},"/v1/projects/{pid}/video/flux/{fid}":{"delete":{"operationId":"supprimer_flux_video","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Flux supprimé : processus tué, dossier retiré"}},"tags":["donnees"]}},"/v1/projects/{pid}/video/flux/{fid}/arreter":{"post":{"operationId":"arreter_flux_video","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Ingestion arrêtée (processus tué, segments conservés)"}},"tags":["donnees"]}},"/v1/projects/{pid}/video/flux/{fid}/demarrer":{"post":{"operationId":"demarrer_flux_video","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Ingestion ffmpeg réellement lancée (503 honnête sans moteur)"}},"tags":["donnees"]}},"/v1/projects/{pid}/video/flux/{fid}/playlist":{"get":{"operationId":"playlist_flux_video","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Playlist HLS des segments réellement présents"}},"tags":["donnees"]}},"/v1/projects/{pid}/video/flux/{fid}/retention":{"get":{"operationId":"lire_retention_video","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rétention courante + segments à purger (complément des récents, époques lues des noms) + playlist après purge"}},"tags":["donnees"]},"put":{"operationId":"poser_retention_video","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetentionVideoReq"}}},"required":true},"responses":{"200":{"description":"Rétention changée (cohérence jours→heures garantie par construction) + vue après changement"}},"tags":["donnees"]}},"/v1/projects/{pid}/video/flux/{fid}/stats":{"post":{"operationId":"stats_flux_video","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"fid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Compteurs MESURÉS au scan du dossier + purge de rétention"}},"tags":["donnees"]}},"/v1/projects/{pid}/video/mosaique":{"post":{"operationId":"composer_mosaique_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MosaiqueReq"}}},"required":true},"responses":{"200":{"description":"Composition DÉCRITE : grille, vignettes en division entière (le reste est rendu, pas caché), segments ALIGNÉS PAR ÉPOQUE (intersection mesurée) et manifeste de sortie — aucun pixel n'est décodé ici"},"400":{"description":"MOSAIQUE_REFUSEE — moins de deux flux, grille ou résolution hors bornes, flux sans segment, raisons nommées"}},"tags":["general"]}},"/v1/projects/{pid}/video/rooms":{"get":{"operationId":"lister_rooms_video","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rooms vivantes et expirées de l'org : état dérivé (vide / en_attente / complete / expiree) sur expiration MESURÉE"}},"tags":["general"]},"post":{"operationId":"creer_room_video","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewRoomReq"}}},"required":true},"responses":{"201":{"description":"Room de signalisation créée : identifiant rom-…, TTL borné, expiration POSÉE à la création — le relais ne transporte QUE de la signalisation, jamais de média"},"400":{"description":"ROOM_REFUSEE — TTL hors bornes ou capacité de rooms atteinte"}},"tags":["general"]}},"/v1/projects/{pid}/video/rooms/{rid}":{"delete":{"operationId":"supprimer_room_video","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"rom-…","in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Room retirée (la signalisation est éphémère)"},"404":{"description":"Room inconnue"}},"tags":["general"]}},"/v1/projects/{pid}/video/rooms/{rid}/offre":{"post":{"operationId":"deposer_offre_room","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SdpReq"}}},"required":true},"responses":{"200":{"description":"Offre déposée (UNE seule par room — la deuxième est refusée, elle écraserait la première)"},"400":{"description":"OFFRE_REFUSEE — room expirée, offre déjà présente, SDP invalide"},"404":{"description":"Room inconnue"}},"tags":["general"]}},"/v1/projects/{pid}/video/rooms/{rid}/reponse":{"post":{"operationId":"deposer_reponse_room","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SdpReq"}}},"required":true},"responses":{"200":{"description":"Réponse déposée (l'offre doit déjà être là — une réponse sans offre ne dit pas à qui elle répond ; réponses plafonnées)"},"400":{"description":"REPONSE_REFUSEE — room expirée, sans offre, réponses pleines ou SDP invalide"},"404":{"description":"Room inconnue"}},"tags":["general"]}},"/v1/projects/{pid}/video/rooms/{rid}/signal":{"get":{"operationId":"relayer_signal_room","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"rid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Signalisation RELAYÉE telle quelle (offre + réponses) pour que les pairs s'entendent DIRECTEMENT — les médias ne passent jamais par le plan de contrôle, dit tel"},"400":{"description":"SIGNAL_REFUSE — room expirée ou sans offre"},"404":{"description":"Room inconnue"}},"tags":["general"]}},"/v1/projects/{pid}/vision":{"get":{"operationId":"lire_vision","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Analyses d'images (en-têtes parsées réelles + OCR moteur)"}},"tags":["llm"]}},"/v1/projects/{pid}/vision/analyses":{"post":{"operationId":"analyser_image","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewAnalyseReq"}}},"required":true},"responses":{"201":{"description":"Image analysée : dimensions parsées + OCR si moteur présent"}},"tags":["llm"]}},"/v1/projects/{pid}/vision/analyses/{vid}":{"delete":{"operationId":"supprimer_analyse_vision","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Analyse retirée"}},"tags":["llm"]}},"/v1/projects/{pid}/vision/diff":{"post":{"operationId":"diff_images_vision","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiffImagesReq"}}},"required":true},"responses":{"200":{"description":"Diff MESURÉ par empreintes de blocs d'octets — les limites de la méthode (aucune décompression) sont énoncées avec le résultat"}},"tags":["llm"]}},"/v1/projects/{pid}/vision/inclinaison":{"post":{"operationId":"inclinaison_vision","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InclinaisonReq"}}},"required":true},"responses":{"200":{"description":"Plan de redressement : angle élu par variance de projection (MESURÉE), confiance = gain sur 0° — aucun pixel décodé ici"}},"tags":["llm"]}},"/v1/projects/{pid}/vision/lot":{"post":{"operationId":"analyser_lot_vision","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotVisionReq"}}},"required":true},"responses":{"200":{"description":"Lot analysé : en-têtes PARSÉS par image, empreintes MESURÉES, doublons rejetés en citant la première occurrence, résultats paginés"}},"tags":["llm"]}},"/v1/projects/{pid}/vision/pdf":{"post":{"operationId":"pdf_vision_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PdfVisionReq"}}},"required":true},"responses":{"200":{"description":"Pages COMPTÉES en clair (/Type/Page) + /Count annoncé en écho (écart dit) + texte OCR par page là où il existe"}},"tags":["llm"]}},"/v1/projects/{pid}/vision/tables":{"post":{"operationId":"detecter_tables_vision","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TablesVisionReq"}}},"required":true},"responses":{"200":{"description":"Tables détectées par les COORDONNÉES des mots : lignes groupées par y, colonnes par bords gauches x (retenues si ≥ 2 lignes consécutives couvertes), une table = ≥ 2 colonnes sécantes sur ≥ 2 lignes — NOTE PIXELS DITE : géométrie uniquement, AUCUN pixel n'est décodé ici (les coordonnées viennent de la sortie OCR)"},"400":{"description":"TABLES_REFUSEES — plus de 10 000 mots, page nulle, seuil non fini ou négatif"}},"tags":["donnees"]}},"/v1/projects/{pid}/vision/yolo":{"post":{"operationId":"detecter_yolo_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/YoloReq"}}},"required":true},"responses":{"200":{"description":"Détections parsées (coordonnées converties en pixels), filtrées par seuil puis NMS PAR CLASSE (tri confiance décroissante, une boîte n'en supprime qu'une même classe), comptage par classe des conservées — chaque nombre est compté"},"400":{"description":"YOLO_REFUSE — ligne mal formée (numéro cité), valeur hors [0,1], seuils hors bornes, dimensions nulles ou trop de détections"}},"tags":["general"]}},"/v1/projects/{pid}/vms":{"get":{"operationId":"lister_vms","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["compute"]},"post":{"operationId":"creer_vm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewVmReq"}}},"required":true},"responses":{"201":{"description":"Créé. PVE configuré : VRAIE qemu (clone cloud-init, specs du catalogue, DHCP réel, phases creation→active). Sans PVE : VM déclarative (etat « declaree », ip « non_applicable ») — provisionnement réel via /v1/projects/{pid}/machines"}},"tags":["compute"]}},"/v1/projects/{pid}/vms/gpu":{"get":{"operationId":"lister_gpu_partages","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"GPU partagés de l'org : attributions posées, fractions engagées/libres (sur 8 par GPU), contentions comptées"}},"tags":["calcul"]}},"/v1/projects/{pid}/vms/gpu/{gpu_id}/admettre":{"post":{"operationId":"admettre_workspace_gpu","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Identifiant du GPU physique (ex. gpu-42)","in":"path","name":"gpu_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdmettreGpuReq"}}},"required":true},"responses":{"200":{"description":"Workspace admis : attribution posée, fractions libres restantes rendues"},"400":{"description":"ADMISSION_REFUSEE — fractions hors bornes, workspace déjà attribué, ou pas assez de fractions LIBRES (contention COMPTÉE, rien n'est posé)"},"404":{"description":"GPU inconnu"}},"tags":["calcul"]}},"/v1/projects/{pid}/vms/gpu/{gpu_id}/liberer":{"post":{"operationId":"liberer_workspace_gpu","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"gpu_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdmettreGpuReq"}}},"required":true},"responses":{"200":{"description":"Fractions libérées (l'inverse exact de l'admission) — attribution rendue"},"400":{"description":"LIBERATION_REFUSEE — workspace sans attribution sur ce GPU"},"404":{"description":"GPU inconnu"}},"tags":["calcul"]}},"/v1/projects/{pid}/vms/plan-clone-lie":{"get":{"operationId":"plan_clone_lie_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Hôte PVE porteur de l'image de base","in":"query","name":"hote","required":true,"schema":{"type":"string"}},{"description":"vmid du template cloud-init cloné","in":"query","name":"template_vmid","required":true,"schema":{"format":"int64","type":"integer"}},{"description":"Première vmid allouée (les clones prennent les ids consécutifs)","in":"query","name":"premiere_vmid","required":true,"schema":{"format":"int64","type":"integer"}},{"description":"Type du catalogue dimensionné sur CHAQUE clone","in":"query","name":"type_vm","required":true,"schema":{"type":"string"}},{"description":"Nombre de clones liés","in":"query","name":"nb_clones","required":true,"schema":{"minimum":0,"type":"integer"}}],"responses":{"200":{"description":"Plan de clones liés : commandes qm rendues, espace total vs full, économie DÉRIVÉE — rien n'est exécuté"},"400":{"description":"PLAN_REFUSE — type inconnu, cible < base, vmid chevauchant, bornes"}},"tags":["compute"]}},"/v1/projects/{pid}/vms/{vid}":{"delete":{"operationId":"supprimer_vm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["compute"]},"get":{"operationId":"detail_vm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["compute"]}},"/v1/projects/{pid}/vms/{vid}/action":{"post":{"operationId":"action_vm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmActionReq"}}},"required":true},"responses":{"200":{"description":"OK"}},"tags":["compute"]}},"/v1/projects/{pid}/vms/{vid}/injection":{"get":{"operationId":"plan_injection_svid","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}},{"description":"Nom de l'app destinataire de la projection","in":"query","name":"app","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Plan d'injection tmpfs : chemins de LECTURE, jamais le secret en variable durable"},"409":{"description":"Aucune SVID à audiences émise pour cette VM"}},"tags":["identite"]}},"/v1/projects/{pid}/vms/{vid}/protection":{"get":{"operationId":"protection_vm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["compute"]},"put":{"operationId":"definir_protection_vm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"OK"}},"tags":["compute"]}},"/v1/projects/{pid}/vms/{vid}/restore":{"post":{"operationId":"restaurer_la_vm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"CU-16 : restaurer une VM (même supprimée) depuis son dernier snapshot.","tags":["compute"]}},"/v1/projects/{pid}/vms/{vid}/restore-test":{"post":{"operationId":"tester_restauration","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"Test de restauration automatique (CU-16) — SIMULATION DÉTERMINISTE :\nla durée est estimée (volumétrie / débit du niveau de protection) et\nle rapport le DIT ; aucune VM éphémère n'est démarrée ni détruite\n(l'ancienne description promettait un démarrage vérifié qui\nn'existait pas — démockisée 15/09).","tags":["compute"]}},"/v1/projects/{pid}/vms/{vid}/snapshots":{"get":{"operationId":"lister_snapshots","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["compute"]},"post":{"operationId":"creer_snapshot_vm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Créé"}},"summary":"Snapshot manuel — incrémental si un parent existe, export S3 pour argent/or.","tags":["compute"]}},"/v1/projects/{pid}/vms/{vid}/svid":{"get":{"operationId":"svid_vm","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"SVID signée (rotation : serial++)"}},"summary":"SVID d'une VM (P5-11) : émission et rotation à la demande.","tags":["identite"]}},"/v1/projects/{pid}/vms/{vid}/svid-audiences":{"post":{"operationId":"emettre_svid_audiences","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SvidAudiencesReq"}}},"required":true},"responses":{"200":{"description":"SVID restreinte à ses audiences (rotation serial++), signée CA ET audiences"}},"tags":["identite"]}},"/v1/projects/{pid}/voix":{"get":{"operationId":"lister_syntheses_voix","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Historique des synthèses + état honnête du moteur de voix"}},"tags":["general"]},"post":{"operationId":"dire_voix","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DireReq"}}},"required":true},"responses":{"201":{"description":"Synthèse réelle locale — WAV 22,05 kHz, durée lue dans l'entête"}},"tags":["general"]}},"/v1/projects/{pid}/voix/catalogue":{"get":{"operationId":"catalogue_voix_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Voix du répertoire des modèles — présence de config et témoin MESURÉES à l'instant"}},"summary":"Catalogue des voix piper disponibles (FLUX W-8) : LECTURE RÉELLE du\nrépertoire des modèles ONNX — ce qui n'est pas sur disque n'est pas\nau catalogue (liste vide honnête si le répertoire est absent). Chaque\nvoix rend son WAV témoin écoutable lorsque la voix a déjà parlé.","tags":["general"]}},"/v1/projects/{pid}/voix/{vid}":{"delete":{"operationId":"supprimer_synthese_voix","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Synthèse et fichier WAV supprimés"}},"tags":["general"]},"get":{"operationId":"detail_synthese_voix","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail d'une synthèse (texte, voix, durées mesurées)"}},"tags":["general"]}},"/v1/projects/{pid}/voix/{vid}/audio":{"get":{"operationId":"audio_synthese_voix","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Le fichier WAV réel (audio/wav)"}},"tags":["general"]}},"/v1/projects/{pid}/volumes":{"get":{"operationId":"lister_volumes","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["stockage"]},"post":{"operationId":"creer_volume","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewVolumeReq"}}},"required":true},"responses":{"201":{"description":"Créé"}},"tags":["stockage"]}},"/v1/projects/{pid}/volumes/tiers":{"get":{"operationId":"catalogue_tiers_volumes","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Catalogue des tiers de performance à QoS contractuelle : IOPS min garantis + plafond Mo/s par tier, plancher premium (10 Go — en dessous le surcoût NVMe n'achète rien, refus ÉCONOMIQUE dit) ; la QoS est RENDUE en manifeste de limites Proxmox/LVM équivalentes et JAMAIS exécutée ici"}},"tags":["stockage"]}},"/v1/projects/{pid}/volumes/{vid}":{"delete":{"operationId":"supprimer_volume","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["stockage"]}},"/v1/projects/{pid}/volumes/{vid}/attach":{"post":{"operationId":"attacher_volume","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"Attaché (relation registre — hors mode réel uniquement, e2e) : corps {message, vm}"},"400":{"description":"vm_id requis dans le corps JSON"},"501":{"description":"ATTACH_NON_SUPPORTE — en mode réel, l'attach d'un volume bloc à une VM n'est pas supporté : le volume existe comme fichier disque réel (voir chemin_reel), aucune pseudo-attach simulée"}},"tags":["stockage"]}},"/v1/projects/{pid}/volumes/{vid}/clone":{"post":{"operationId":"cloner_volume_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClonerVolumeReq"}}},"required":true},"responses":{"201":{"description":"Clone copy-on-write créé — 0 octet copié à la création, l'occupation monte à mesure que les blocs divergent"}},"tags":["stockage"]}},"/v1/projects/{pid}/volumes/{vid}/clones":{"get":{"operationId":"lister_clones_volume","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Clones CoW du volume, occupation MESURÉE (octets copiés)"}},"tags":["stockage"]}},"/v1/projects/{pid}/volumes/{vid}/detach":{"post":{"operationId":"detacher_volume","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["stockage"]}},"/v1/projects/{pid}/volumes/{vid}/politique-snapshots":{"get":{"operationId":"lire_politique_snapshots","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Politique + plan courant (rotation : jamais le plus récent)"},"404":{"description":"Aucune politique posée"}},"tags":["stockage"]},"put":{"operationId":"poser_politique_snapshots","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolitiqueSnapshotsReq"}}},"required":true},"responses":{"200":{"description":"Politique posée + plan IMMÉDIAT (dû ? rotation ? — observé sur les dates mesurées)"}},"tags":["stockage"]}},"/v1/projects/{pid}/volumes/{vid}/snapshots":{"get":{"operationId":"lister_snapshots_volume","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Snapshots du volume + plan courant si une politique existe"}},"tags":["stockage"]},"post":{"operationId":"creer_snapshot_volume","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Snapshot pris MAINTENANT — taille MESURÉE sur le support (fichier sparse : occupation réelle)"}},"tags":["stockage"]}},"/v1/projects/{pid}/volumes/{vid}/snapshots/{sid}":{"delete":{"operationId":"supprimer_snapshot_volume","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"vid","required":true,"schema":{"type":"string"}},{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Snapshot retiré du registre"}},"tags":["stockage"]}},"/v1/projects/{pid}/volumes/{vid}/tier":{"post":{"operationId":"changer_tier_volume","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Identifiant du volume (vol-…)","in":"path","name":"vid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TierVolumeReq"}}},"required":true},"responses":{"200":{"description":"Changement de tier VALIDÉ contre le volume réel : le manifeste de limites Proxmox/LVM équivalentes est RENDU (lvcreate + iops min + plafond débit) et JAMAIS exécuté — aucun lvcreate ici, l'application appartient à l'intégration hyperviseur ; le tier est posé dans le registre de l'org"},"400":{"description":"TIER_REFUSE — refus NOMMÉ : tier inconnu (cité, admis listés), ou refus ÉCONOMIQUE premium-nvme sous 10 Go (le surcoût NVMe n'achète rien sur un petit volume, prenez standard-ssd)"},"404":{"description":"Volume inconnu dans l'org"}},"tags":["stockage"]}},"/v1/projects/{pid}/vouchers":{"get":{"operationId":"vouchers_org","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Vouchers de l'organisation : code, solde, expiration"}},"tags":["gouvernance"]}},"/v1/projects/{pid}/vpn/ha/{site}":{"put":{"operationId":"poser_site_ha_vpn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"site","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteVpnHaReq"}}},"required":true},"responses":{"200":{"description":"Site HA posé (exactement DEUX gateways, santé MESURÉE apportée)"},"400":{"description":"SITE_HA_INVALIDE — pas deux gateways"}},"tags":["reseau"]}},"/v1/projects/{pid}/vpn/ha/{site}/chemin-effectif":{"get":{"operationId":"chemin_effectif_vpn_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"site","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Chemin effectif MESURÉ : la préférée si disponible, bascule sur le secours sinon (motif rendu) ; les deux muettes → refus nommé"},"404":{"description":"Site HA inconnu"}},"tags":["reseau"]}},"/v1/projects/{pid}/vpn/mesh":{"get":{"operationId":"lire_mesh_vpn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Mesh WireGuard courant (configs par site, pairs avec CLÉS PUBLIQUES uniquement)"}},"tags":["reseau"]},"post":{"operationId":"generer_mesh_vpn_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeshVpnReq"}}},"required":true},"responses":{"201":{"description":"Full-mesh DÉRIVÉ : chaque site reçoit un pair par autre site (AllowedIPs = préfixe du pair) — CLÉS PUBLIQUES uniquement, la privée reste chez son site"},"400":{"description":"MESH_INVALIDE — site invalide, doublons, préfixes chevauchants"}},"tags":["reseau"]}},"/v1/projects/{pid}/vpn/mesh/{site}/config":{"get":{"operationId":"config_site_mesh_vpn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"site","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Sections [Peer] du site : clé publique, endpoint, AllowedIPs — la clé PRIVÉE du site ne figure JAMAIS dans ce rendu"},"404":{"description":"Site inconnu du mesh (ou aucun mesh)"}},"tags":["reseau"]}},"/v1/projects/{pid}/vpn/peers":{"get":{"operationId":"lister_peers_vpn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Peers cirroVPN de l'organisation (sans secrets)"}},"tags":["reseau"]},"post":{"operationId":"creer_peer_vpn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewPeerReq"}}},"required":true},"responses":{"201":{"description":"Peer créé — config client .conf rendue UNE fois"}},"tags":["reseau"]}},"/v1/projects/{pid}/vpn/peers/{peer_id}":{"delete":{"operationId":"supprimer_peer_vpn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"peer_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Peer retiré de l'interface"}},"tags":["reseau"]}},"/v1/projects/{pid}/vpn/peers/{peer_id}/config":{"get":{"operationId":"config_peer_vpn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"peer_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Fichier .conf WireGuard du peer (prêt à importer)"}},"summary":"Retélécharge le .conf client d'un peer : la clé privée est stockée\nchiffrée au repos et n'est reconstituée qu'ici (droit project.read).","tags":["reseau"]}},"/v1/projects/{pid}/vpn/peers/{peer_id}/profil":{"put":{"operationId":"poser_profil_vpn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"peer_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoserProfilVpnReq"}}},"required":true},"responses":{"200":{"description":"Profil posé/mis à jour — la génération de .conf exigera un profil valide (exiger_valide)"}},"tags":["reseau"]}},"/v1/projects/{pid}/vpn/profils":{"get":{"operationId":"lister_profils_vpn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Statuts temporels MESURÉS des profils (révoqué > échu > actif ; échéance 0 = jamais)"}},"tags":["reseau"]}},"/v1/projects/{pid}/vpn/tunnels":{"get":{"operationId":"etats_tunnels_vpn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"États de tunnels dérivés (bon | degrade | muet) — un seul relevé = pas d'état, dit tel quel"}},"tags":["reseau"]}},"/v1/projects/{pid}/vpn/tunnels/{peer_id}/releve":{"post":{"operationId":"releve_tunnel_vpn","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"peer_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleveTunnelReq"}}},"required":true},"responses":{"200":{"description":"Relevé brut enregistré (compteurs monotones wg) + état dérivé des DEUX derniers relevés"}},"tags":["reseau"]}},"/v1/projects/{pid}/vue-ensemble":{"get":{"operationId":"vue_ensemble_org","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VueEnsembleResume"}}},"description":"Première page d'une organisation : compteurs RÉELS de chaque service + budget + coût du mois + coût mensuel PAR RESSOURCE (cout_ressources : lignes aux tarifs de la facture, total, familles non tarifées). B3 (audit 16/09) : la WebUI affiche cout_ressources.total comme SEUL coût du mois (cout_mois — somme facture — reste servi pour compatibilité). B6 : budget/usage en vcpus / ram_gb (Go, arrondis au supérieur) / disque_gb (Go) — les quantités RAM en Mo sont servies sous ram_mb (alias ram_mo) ailleurs dans l'API."},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Jeton absent/invalide"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"HORS_ORGANISATION / PERMISSION_REFUSEE — project.read requis"}},"summary":"Vue d'ensemble ORGANISATION : un seul appel qui compte chaque\nservice du projet dans ses registres réels — jamais de chiffre\ninventé, un service absent vaut 0.","tags":["systeme"]}},"/v1/projects/{pid}/warehouses":{"get":{"operationId":"lister_warehouses","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["donnees"]},"post":{"operationId":"creer_warehouse","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewWarehouseReq"}}},"required":true},"responses":{"201":{"description":"Créé : pod ferroquery provisionné (cluster plateforme), compte admin créé dans le pod, host-record dnsmasq posé — jeton rendu UNE fois dans la réponse"},"503":{"description":"Rollout k8s ou add-user échoué (rollback complet effectué)"}},"tags":["donnees"]}},"/v1/projects/{pid}/warehouses/{wid}":{"delete":{"operationId":"supprimer_warehouse","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé : pod + PVC k8s détruits, host-record dnsmasq retiré, registre défaits (tolérant)"}},"tags":["donnees"]}},"/v1/projects/{pid}/warehouses/{wid}/file":{"get":{"operationId":"ordonnancer_file_warehouse","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Entrepôt (wh-…)","in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"La file de l'entrepôt et son ORDRE DE SERVICE : tri STABLE par (priorité EFFECTIVE, âge) — le garde anti-famine DIT (après 5 tours d'attente OBSERVÉS, la priorité effective monte d'un cran, plafonnée à haute)"}},"tags":["donnees"]},"post":{"operationId":"admettre_file_warehouse","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Entrepôt (wh-…)","in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdmettreFileReq"}}},"required":true},"responses":{"201":{"description":"Requête ADMISE dans la file de l'entrepôt : à saturation, la règle DITE s'applique — la plus ancienne active de priorité 5 est PRÉEMPTÉE au profit d'une strictement plus prioritaire, sinon refus « file pleine » nommé avec les comptes"},"400":{"description":"FILE_REFUSEE — priorité hors 1-5, ou saturation sans priorité 5 à préempter (raison et comptes cités)"},"404":{"description":"Entrepôt inconnu de l'org"}},"tags":["donnees"]}},"/v1/projects/{pid}/warehouses/{wid}/ingest":{"post":{"operationId":"ingester_warehouse","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngestWhReq"}}},"required":true},"responses":{"501":{"description":"Chargement non supporté par le moteur — utiliser l'API de l'entrepôt directement"}},"summary":"Ingestion d'un bucket S3 : NON SUPPORTÉ par le moteur ferroquery\n(v1.103 — il charge des fichiers locaux via `ferroquery load\n--file` et n'a aucun client S3 ; insertAll exige un schéma connu).\n501 explicite avec la porte de sortie réelle, à la place de\nl'ancienne ingestion FICTICE (compteur de tables et Go fabriqués).","tags":["donnees"]}},"/v1/projects/{pid}/warehouses/{wid}/ingestion-s3":{"post":{"operationId":"plan_ingestion_s3","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanIngestionS3Req"}}},"required":true},"responses":{"200":{"description":"Plan d'ingestion : 4 étapes numérotées avec commandes exactes + lignes attendues quand le contenu est fourni"}},"tags":["donnees"]}},"/v1/projects/{pid}/warehouses/{wid}/query":{"post":{"operationId":"requete_warehouse","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequeteWhReq"}}},"required":true},"responses":{"200":{"description":"Résultat du moteur réel (colonnes du schéma rendu, lignes de rows)"},"503":{"description":"Entrepôt non provisionné ou pod injoignable"}},"summary":"Exécute une requête sur le VRAI moteur ferroquery de l'entrepôt\n(EX-DB-008) : `POST /bq/v2/…/queries` sur le NodePort du pod,\njeton Bearer du compte créé à la provision, délai ~10 s. Le\nrésultat est celui du moteur — même vide ; un entrepôt injoignable\nest une erreur honnête, plus jamais de lignes inventées.","tags":["donnees"]}},"/v1/projects/{pid}/warehouses/{wid}/vues":{"get":{"operationId":"lister_vues_warehouse","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"wh-…","in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Vues matérialisées de l'entrepôt : dépendance déclarée et dernier refresh (None = jamais rafraîchie, dit tel)"}},"tags":["donnees"]},"post":{"operationId":"creer_vue_warehouse","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewVueReq"}}},"required":true},"responses":{"201":{"description":"Vue déclarée (jamais rafraîchie : dernier_refresh_le = None, honnête) ; un cycle de dépendances éventuel est refusé NOMMÉMENT avec sa chaîne"},"400":{"description":"VUE_INVALIDE — identifiants ferroquery des deux côtés, ou cycle"},"409":{"description":"DEJA_PRIS — une vue porte déjà ce nom"}},"tags":["donnees"]}},"/v1/projects/{pid}/warehouses/{wid}/vues/dependances":{"get":{"operationId":"dependances_vues_warehouse","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}},{"description":"Table amont dont on cherche les dépendantes","in":"query","name":"table","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Vues qui dépendent de cette table — DIRECTEMENT ou par vue amont (transitives) ; un cycle dans les dépendances est refusé NOMMÉMENT avec sa chaîne"},"400":{"description":"DEPENDANCES_INVALIDES — cycle nommé ou paramètre absent"}},"tags":["donnees"]}},"/v1/projects/{pid}/warehouses/{wid}/vues/{nom}/refresh":{"post":{"operationId":"refresh_vue_warehouse","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}},{"in":"path","name":"nom","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshVueReq"}}},"required":true},"responses":{"200":{"description":"Plan de refresh incrémental : delta = lignes STRICTEMENT plus récentes que le dernier refresh (tout est delta au premier tour), intégration BORNÉE au plafond, reste reporté, cohérence en % — le dernier refresh n'est marqué QUE si tout est intégré"},"400":{"description":"REFRESH_REFUSE — horodatage illisible"},"404":{"description":"Vue inconnue"}},"tags":["donnees"]}},"/v1/projects/{pid}/webhooks":{"get":{"operationId":"lister_webhooks","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["automatisation"]},"post":{"operationId":"creer_webhook","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewWebhookReq"}}},"required":true},"responses":{"201":{"description":"Créé"}},"tags":["automatisation"]}},"/v1/projects/{pid}/webhooks-org":{"get":{"operationId":"lister_webhooks_org","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Webhooks configurés au niveau organisation"}},"tags":["systeme"]},"post":{"operationId":"creer_webhook_org","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewWebhookOrgReq"}}},"required":true},"responses":{"200":{"description":"Webhook créé (secret affiché UNE fois)"}},"tags":["systeme"]}},"/v1/projects/{pid}/webhooks-org/{wid}":{"delete":{"operationId":"supprimer_webhook_org","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Webhook supprimé"}},"tags":["systeme"]}},"/v1/projects/{pid}/webhooks/{wid}":{"delete":{"operationId":"supprimer_webhook","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Supprimé"}},"tags":["automatisation"]}},"/v1/projects/{pid}/webhooks/{wid}/replay":{"post":{"operationId":"rejouer_webhook","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"Rejoue les livraisons en échec d'un webhook (file de rejeu, EX-OBS-005).","tags":["automatisation"]}},"/v1/projects/{pid}/webhooks/{wid}/test":{"post":{"operationId":"tester_webhook","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"tags":["automatisation"]}},"/v1/projects/{pid}/workflows":{"get":{"operationId":"lister_workflows","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Workflows de l'organisation (avec dernière exécution)"}},"tags":["general"]},"post":{"operationId":"creer_workflow","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewWorkflowReq"}}},"required":true},"responses":{"201":{"description":"Workflow créé"}},"tags":["general"]}},"/v1/projects/{pid}/workflows/argo/etat":{"get":{"operationId":"etat_argo_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"État RÉEL des pods du namespace argo lus du cluster — « jamais déployé » rendu comme tel ; en harnais (CIRRO_WORKLOADS_REELS=0) l'état cluster n'est pas consulté, jamais simulé"},"503":{"description":"CLUSTER_INJOIGNABLE — le canal kubectl ne répond pas, aucun état inventé"}},"tags":["general"]}},"/v1/projects/{pid}/workflows/argo/offre":{"get":{"operationId":"offre_argo","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Offre honnête : Argo Workflows 4.0 épinglé (Apache-2.0, CNCF gradué, k8s-natif), fonctions mesurables, dépendances citées, plan d'installation exact avec sa garde, positionnement deux étages (moteur maison = API-native léger, Argo = DAG conteneurisés lourds)"}},"tags":["general"]}},"/v1/projects/{pid}/workflows/{wid}":{"delete":{"operationId":"supprimer_workflow","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Workflow supprimé"}},"tags":["general"]}},"/v1/projects/{pid}/workflows/{wid}/argo":{"get":{"operationId":"manifeste_argo_workflow","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"description":"Identifiant du workflow (wf-…)","in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Manifeste Argo Workflow généré du workflow existant : un template par étape (webhook/fonction/topic → HTTP, attendre → suspend), tentatives/backoff portés en retryStrategy, conditions en when sur la sortie précédente ; les URL de fonction/topic sont des SUBSTITUTIONS explicites et les catchs (erreur_vers) sont DITS (exit handler Argo)"},"404":{"description":"Workflow inconnu dans l'org"}},"tags":["general"]}},"/v1/projects/{pid}/workflows/{wid}/executions":{"get":{"operationId":"lister_executions","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Exécutions du workflow"}},"tags":["general"]},"post":{"operationId":"demarrer_workflow","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Exécution démarrée (en_cours)"}},"tags":["general"]}},"/v1/projects/{pid}/workflows/{wid}/executions/{eid}":{"delete":{"operationId":"annuler_execution","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}},{"in":"path","name":"eid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Exécution annulée"}},"tags":["general"]},"post":{"operationId":"ticker_execution","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}},{"in":"path","name":"eid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Exécution avancée d'UNE action (tick)"}},"tags":["general"]}},"/v1/projects/{pid}/workspaces":{"get":{"operationId":"lister_workspaces","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Workspaces du projet"}},"summary":"Workspaces code-server (P5-10) : VM dédiée + proxy + tunnel.","tags":["apps"]},"post":{"operationId":"creer_workspace","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewWorkspaceReq"}}},"required":true},"responses":{"201":{"description":"Workspace créé : VM + code-server + tunnel"}},"tags":["apps"]}},"/v1/projects/{pid}/workspaces/{wid}":{"delete":{"operationId":"supprimer_workspace","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Workspace supprimé — workload pod retiré (par étiquette) + entrée registre"},"404":{"description":"Workspace inconnu (ou pas un workspace)"}},"summary":"Mi5 (audit 14/09) : les workspaces n'avaient pas de route DELETE —\nun code-server créé ne pouvait plus être détruit par l'API (que par\nle détour générique /apps/{id}). Supprime le workload pod (mécanique\npar étiquette — tolérante si le workload a déjà disparu) ET retire\nl'entrée du registre des apps (le marqueur workspace est le\ntunnel_token).","tags":["apps"]}},"/v1/projects/{pid}/workspaces/{wid}/etats":{"get":{"operationId":"lister_etats_workspace","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"États nommés du workspace"}},"tags":["apps"]},"post":{"operationId":"creer_etat_workspace_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreerEtatReq"}}},"required":true},"responses":{"201":{"description":"État nommé capturé (chemins validés, doublons refusés, rotation observée)"}},"tags":["apps"]}},"/v1/projects/{pid}/workspaces/{wid}/etats/diff":{"get":{"operationId":"diff_etats_workspace_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}},{"in":"query","name":"reference","required":true,"schema":{"type":"string"}},{"in":"query","name":"actuel","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Diff par empreintes : ajoutés / supprimés / modifiés"}},"tags":["apps"]}},"/v1/projects/{pid}/workspaces/{wid}/etats/restauration":{"get":{"operationId":"plan_restauration_workspace_route","parameters":[{"description":"Slug de l'organisation — les projets sont un groupement logique, les ressources se gèrent au niveau de l'org","in":"path","name":"pid","required":true,"schema":{"type":"string"}},{"in":"path","name":"wid","required":true,"schema":{"type":"string"}},{"in":"query","name":"cible","required":true,"schema":{"type":"string"}},{"in":"query","name":"actuel","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"PLAN de restauration (restaurés / SUPPRIMÉS / modifiés / inchangés) — la copie réelle vers la VM reste à l'opérateur"}},"tags":["apps"]}},"/v1/public/portail/{org}":{"get":{"operationId":"portail_page_publique","parameters":[{"in":"path","name":"org","required":true,"schema":{"type":"string"}},{"description":"json : index machine au lieu du HTML","in":"query","name":"format","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"HTML autonome (ou JSON avec ?format=json) — adresses sans secrets, CSP + ETag"}},"summary":"Page publique du portail d'une organisation — Homer intégré au produit.","tags":["public"]}},"/v1/public/restore-tests":{"get":{"operationId":"rapport_public_restauration","responses":{"200":{"description":"OK"}},"summary":"Rapport PUBLIC de tests de restauration (CU-16) — la preuve de la\nrécupérabilité, consultable sans compte.","tags":["public"]}},"/v1/public/security-score/{org}":{"get":{"operationId":"score_securite_publique","parameters":[{"in":"path","name":"org","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"Score sécurité public (NF-SEC-016) : sans authentification ni détail\nsensible — score, verdict et statut des contrôles uniquement.","tags":["public"]}},"/v1/public/status/{org}":{"get":{"operationId":"status_page_publique","parameters":[{"in":"path","name":"org","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"summary":"État public d'une organisation (sans auth) : composants, incidents,\ndisponibilité simulée — la page à communiquer aux clients.","tags":["public"]}},"/v1/public/status/{org}/badge.svg":{"get":{"operationId":"badge_statut_publique","parameters":[{"in":"path","name":"org","required":true,"schema":{"type":"string"}},{"description":"json : structure du badge au lieu du SVG","in":"query","name":"format","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Badge SVG shields-like (ou JSON) — état global calculé depuis des comptages mesurés"}},"summary":"Badge de statut public (FLUX W-3) : mêmes MESURES que la page de\nstatut (VMs/bases/k8s actifs, anomalies ouvertes) réduites à un état\nglobal par `etats::etat_global_depuis` — intégrable par\n`<img src=\"/v1/public/status/{org}/badge.svg\">`, `?format=json` rend\nla structure. Une org sans composant déclaré est « inconnu », jamais\n« operationnel » par devinette.","tags":["public"]}},"/v1/public/support/stats/{plan}":{"get":{"operationId":"stats_publiques_support","parameters":[{"description":"basic | argent | or","in":"path","name":"plan","required":true,"schema":{"type":"string"}},{"description":"Fenêtre (jours, 1-90)","in":"query","name":"fenetre_jours","required":true,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"description":"Agrégats publics du plan sur TOUTES les orgs qui l'ont actif — anonymisés par construction"}},"tags":["gouvernance"]}},"/v1/quota-requests":{"get":{"operationId":"toutes_demandes_quota","responses":{"200":{"description":"TOUTES les demandes de TOUTES les organisations (superadmin)"}},"tags":["quotas"]}},"/v1/sandbox/purge":{"post":{"operationId":"purger_sandboxes_expirees","responses":{"200":{"description":"OK"}},"summary":"Purge les projets sandbox expirés (appelé périodiquement ou à la demande).","tags":["automatisation"]}},"/v1/spiffe/bundle":{"get":{"operationId":"bundle_spiffe","responses":{"200":{"description":"Clé publique CA + métadonnées"}},"summary":"Bundle de confiance SPIFFE (P5-11) — public par design : n'importe\nquel workload du LAN vérifie les SVID hors ligne.","tags":["identite"]}},"/v1/superadmin/bascule":{"get":{"operationId":"etat_bascule","responses":{"200":{"description":"Site actif + santé des sites selon les hosts enregistrés"}},"tags":["superadmin"]},"post":{"operationId":"basculer_site","requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"Site actif changé — les prochaines créations de machine passent par un host de ce site"}},"tags":["superadmin"]}},"/v1/superadmin/demandes-org":{"get":{"operationId":"lister_demandes_org","responses":{"200":{"description":"Demandes d'organisations (tous états)"}},"tags":["gouvernance"]}},"/v1/superadmin/demandes-org/{id}/approuver":{"post":{"operationId":"approuver_demande_org","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Organisation + compte admin créés — mot de passe temporaire rendu UNE SEULE fois"}},"summary":"Approbation : crée l'organisation, le compte utilisateur (si l'email\nn'existe pas déjà — sinon simple adhésion) avec le rôle admin, et le\ncontact officiel. Le mot de passe temporaire est rendu une seule\nfois, à communiquer hors bande ; l'utilisateur le changera.","tags":["gouvernance"]}},"/v1/superadmin/demandes-org/{id}/refuser":{"post":{"operationId":"refuser_demande_org","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefuserReq"}}},"required":true},"responses":{"200":{"description":"Demande refusée avec motif"}},"tags":["gouvernance"]}},"/v1/superadmin/dns-bascule":{"get":{"operationId":"etat_dns_bascule","responses":{"200":{"description":"Bascule DNS automatique : configuration, santé des sites (mesurée), cohérence des records"}},"tags":["superadmin"]},"put":{"operationId":"configurer_dns_bascule","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigBasculeDns"}}},"required":true},"responses":{"200":{"description":"Configuration remplacée (mappings FQDN → IP par site)"}},"tags":["superadmin"]}},"/v1/superadmin/dns-bascule/resynchroniser":{"post":{"operationId":"resynchroniser_dns_bascule","responses":{"200":{"description":"Records A forcés vers l'IP du site ACTIF (réparation de dérive, sans changer de site)"}},"tags":["superadmin"]}},"/v1/superadmin/hosts":{"get":{"operationId":"lister_hosts","responses":{"200":{"description":"Hosts Proxmox enregistrés : stats directes, usage, disponibilité, GPU. B6 (audit 16/09) : les quantités RAM en Mo sont servies sous ram_mb ET son alias historique ram_mo (usage, disponible, capacite_physique, consolide) ; les plafonds restent en Go (quotas.ram_gb)"}},"tags":["superadmin"]},"post":{"operationId":"creer_host","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewHostReq"}}},"required":true},"responses":{"201":{"description":"Host enregistré (stats lues à chaud si joignable)"}},"tags":["superadmin"]}},"/v1/superadmin/hosts/{hid}":{"delete":{"operationId":"supprimer_host","parameters":[{"in":"path","name":"hid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Host retiré de l'inventaire (les machines PVE existantes ne sont pas touchées)"}},"tags":["superadmin"]},"put":{"operationId":"modifier_host","parameters":[{"in":"path","name":"hid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"Host modifié (nom du nœud, URL, site, actif, par défaut, GPU, note, jeton en rotation)"}},"tags":["superadmin"]}},"/v1/superadmin/hosts/{hid}/quota":{"put":{"operationId":"quota_host","parameters":[{"in":"path","name":"hid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaHost"}}},"required":true},"responses":{"200":{"description":"Plafond d'allocation du host fixé"}},"tags":["superadmin"]}},"/v1/superadmin/hosts/{hid}/test":{"post":{"operationId":"tester_host","parameters":[{"in":"path","name":"hid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Joignabilité réelle testée (jamais de faux « en ligne »)"}},"tags":["superadmin"]}},"/v1/superadmin/impersonation/stop":{"post":{"operationId":"stop_impersonation","parameters":[{"description":"Org de la session terminée (audit)","in":"query","name":"slug","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Retour de main JOURNALISÉ — le jeton impersoné (JWT stateless) n'est pas révocable serveur : il expire seul en ≤ 30 min"},"403":{"description":"SUPERADMIN_REQUIS — s'appelle avec le jeton superadmin d'origine"}},"tags":["gouvernance"]}},"/v1/superadmin/materiel":{"get":{"operationId":"materiel_superadmin","responses":{"200":{"description":"Inventaire complet : composants actifs + stock + suggestions"}},"tags":["materiel"]}},"/v1/superadmin/materiel/stock":{"post":{"operationId":"ajouter_stock","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewStockReq"}}},"required":true},"responses":{"200":{"description":"Composant ajouté au stock (superadmin)"}},"tags":["materiel"]}},"/v1/superadmin/materiel/stock/{sid}":{"delete":{"operationId":"retirer_stock","parameters":[{"in":"path","name":"sid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Composant retiré du stock"}},"tags":["materiel"]}},"/v1/superadmin/organisations/importer":{"post":{"operationId":"importer_org","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReqImport"}}},"required":true},"responses":{"200":{"description":"Rapport d'import par famille (+ objets_restores / secrets_restores / secrets_en_echec pour un bundle v2 « configuration_donnees »)"},"400":{"description":"Bundle invalide (format/version/mode/empreinte) ou slug cible invalide"}},"tags":["iam"]}},"/v1/superadmin/orgs/{slug}":{"delete":{"operationId":"supprimer_org","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}},{"description":"Doit répéter le slug exact — sinon 400","in":"query","name":"confirmer","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rapport de destruction : machines PVE, instances, namespace k8s, clés meta, membres"},"400":{"description":"Confirmation manquante ou divergente"},"404":{"description":"Organisation introuvable"}},"summary":"Supprime DÉFINITIVEMENT une organisation et tout ce qu'elle possède :\nmachines Proxmox réelles (garde anti-collision par tag org), instances\nlocales arrêtées (namespace k8s supprimé — workloads et services partent\navec), chaque clé meta de la famille de l'org, vouchers et demandes\nretirés des listes globales, adhésions, projets, ligne org. L'audit\nreste : la chaîne HMAC ne se tronque jamais.","tags":["gouvernance"]}},"/v1/superadmin/orgs/{slug}/contact":{"put":{"operationId":"maj_contact_org","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactReq"}}},"required":true},"responses":{"200":{"description":"Contact de l'organisation mis à jour"}},"tags":["gouvernance"]}},"/v1/superadmin/orgs/{slug}/etat":{"post":{"operationId":"basculer_etat_org","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EtatOrgReq"}}},"required":true},"responses":{"200":{"description":"État posé — une org suspendue est en lecture seule intégrale (toutes écritures refusées)"},"400":{"description":"État invalide"},"404":{"description":"Organisation introuvable"}},"summary":"Active ou suspend une organisation. Suspendue = lecture seule\npartout (le garde vit dans `projet_et_role` : une seule place,\ntoutes les routes d'écriture le traversent).","tags":["gouvernance"]}},"/v1/superadmin/orgs/{slug}/facture":{"get":{"operationId":"facture_org","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Facture du mois CALCULÉE en direct (usage réel × tarifs showback × 730 h)"}},"tags":["gouvernance"]}},"/v1/superadmin/orgs/{slug}/facture/emettre":{"post":{"operationId":"emettre_facture","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Facture émise (snapshot du calcul, numéro séquentiel)"}},"tags":["gouvernance"]}},"/v1/superadmin/orgs/{slug}/factures":{"get":{"operationId":"lister_factures","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Factures émises (snapshots)"}},"tags":["gouvernance"]}},"/v1/superadmin/orgs/{slug}/gestion":{"get":{"operationId":"gestion_org","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tout ce qu'il faut gérer une org : contact, quota+usage, ressources, tickets ouverts, factures"}},"summary":"Vue de gestion agrégée : contact officiel, quota (budget plateforme)\net usage réel, inventaire des ressources, tickets ouverts et\ndernières factures — un seul appel pour l'écran de gestion.","tags":["gouvernance"]}},"/v1/superadmin/orgs/{slug}/impersoner":{"post":{"operationId":"impersoner_org","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Jeton admin-org de CETTE org : 30 min, claims impersonation=true + impersone_par ; toute l'API suivante voit un admin normal de l'org (jamais superadmin)"},"403":{"description":"SUPERADMIN_REQUIS — (aussi : un jeton d'impersonation ne peut pas en émettre un autre)"},"404":{"description":"Organisation introuvable"},"409":{"description":"AUCUN_ADMIN_ELIGIBLE — aucun admin-org incarnable (ni admin plateforme ni superadmin)"}},"summary":"FIN2-SA-001 — Impersonation : émet un jeton qui agit comme un admin\nNORMAL de l'org cible (30 min, claims `impersonation: true` +\n`impersone_par`). Le jeton est émis POUR un admin-org de l'org qui\nn'est ni admin plateforme ni superadmin : c'est son compte qui porte\nles droits — aucune voie d'élévation, l'accès superadmin reste refusé\n(403) tant que le jeton vit.","tags":["gouvernance"]}},"/v1/superadmin/orgs/{slug}/quotas":{"get":{"operationId":"lire_quotas_superadmin","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Quotas effectifs + utilisation + plafonds (même lecture que GET /v1/projects/{pid}/quotas)"},"403":{"description":"SUPERADMIN_REQUIS"},"404":{"description":"Organisation introuvable"}},"summary":"FIN2-SA-003 — Lecture superadmin des quotas d'une org : MÊME source\nde vérité que `GET /v1/projects/{pid}/quotas` (meta `quotas:{slug}`,\nrepli défauts signalé) + utilisation réelle + plafonds.","tags":["quotas"]},"put":{"operationId":"definir_quotas_superadmin","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DefinitionQuotasReq"}}},"required":true},"responses":{"200":{"description":"Quotas écrits — effet immédiat, reflété par GET /v1/projects/{pid}/quotas"},"400":{"description":"DEFINITION_INVALIDE — champ inconnu, non positif, au-delà du plafond, sous l'utilisation actuelle ou aucun champ fourni"},"403":{"description":"SUPERADMIN_REQUIS"},"404":{"description":"Organisation introuvable"}},"summary":"FIN2-SA-003 — Écriture DIRECTE des quotas d'une org (console\nsuperadmin) : bornes = validation `valider_definition` (positif, ≤\nplafond plateforme, ≥ utilisation actuelle, au moins un champ).\nMême clé de vérité que la vue org : meta `quotas:{slug}` — la vue\n`GET /v1/projects/{pid}/quotas` reflète immédiatement.","tags":["quotas"]}},"/v1/superadmin/orgs/{slug}/utilisateurs":{"get":{"operationId":"lister_utilisateurs_org","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Comptes de l'org : id (usr-…), email, rôle, mfa, created_at (adhésion, RFC3339)"},"403":{"description":"SUPERADMIN_REQUIS"},"404":{"description":"Organisation introuvable"}},"summary":"FIN2-SA-002 — Comptes d'une organisation (vue superadmin) : chaque\nmembre avec son id utilisateur, son rôle dans l'org, son statut MFA\net la date d'adhésion. Sources : `members` + `users` du store (même\nlecture que /v1/organizations/{org}/members, augmentée du MFA).","tags":["gouvernance"]}},"/v1/superadmin/orgs/{slug}/utilisateurs/{id}":{"delete":{"operationId":"supprimer_utilisateur_org","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}},{"description":"Identifiant utilisateur (usr-…) ou email (« @ » encodé %40 dans le chemin)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Compte retiré de CETTE org (le compte et ses autres appartenances restent)"},"400":{"description":"LIMITE — dernier admin de l'org : refuse de laisser l'org sans admin"},"403":{"description":"SUPERADMIN_REQUIS"},"404":{"description":"Organisation inconnue, compte inconnu ou non-membre de l'org"}},"summary":"FIN2-SA-002 — Retire un compte d'UNE organisation (superadmin) : le\nretrait ne touche QUE l'appartenance — le compte utilisateur et ses\nautres appartenances restent. Refus 400 `LIMITE` sur le DERNIER admin\nde l'org (une org sans aucun admin serait orpheline).","tags":["gouvernance"]}},"/v1/superadmin/tickets":{"get":{"operationId":"lister_tickets_plateforme","parameters":[{"description":"filtrer par organisation","in":"query","name":"org","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tous les tickets de support, toutes organisations"}},"tags":["gouvernance"]}},"/v1/superadmin/traces":{"get":{"operationId":"lister_traces_systeme","responses":{"200":{"description":"Traces « system » (auth, scheduler, superadmin) — superadmin uniquement"}},"tags":["systeme"]}},"/v1/superadmin/vouchers":{"get":{"operationId":"lister_vouchers","parameters":[{"description":"filtrer par organisation","in":"query","name":"org","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Vouchers (tous états)"}},"tags":["gouvernance"]},"post":{"operationId":"creer_voucher","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewVoucherReq"}}},"required":true},"responses":{"201":{"description":"Voucher créé — code lisible rendu une fois (visible aussi dans la liste)"}},"tags":["gouvernance"]}},"/v1/superadmin/vouchers/{code}/attribuer":{"post":{"operationId":"attribuer_voucher","parameters":[{"in":"path","name":"code","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttribuerReq"}}},"required":true},"responses":{"200":{"description":"Voucher attribué à l'organisation"}},"tags":["gouvernance"]}},"/v1/superadmin/vouchers/{code}/revoquer":{"post":{"operationId":"revoquer_voucher","parameters":[{"in":"path","name":"code","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Voucher révoqué (solde gelé, plus consommable)"}},"tags":["gouvernance"]}},"/v1/superadmin/vue-ensemble":{"get":{"operationId":"vue_ensemble_superadmin","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VueEnsembleSuperadmin"}}},"description":"Toutes les organisations avec quotas, usage et ressources (superadmin)"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"Jeton absent/invalide"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"SUPERADMIN_REQUIS — réservé aux superadmins"}},"tags":["systeme"]}},"/v1/superadmins":{"get":{"operationId":"lister_superadmins","responses":{"200":{"description":"Liste des superadmins (mode implicite si vide)"}},"tags":["quotas"]},"post":{"operationId":"promouvoir_superadmin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewSuperadminReq"}}},"required":true},"responses":{"201":{"description":"Superadmin promu — la liste devient explicite"}},"tags":["quotas"]}},"/v1/superadmins/{email}":{"delete":{"operationId":"retrograder_superadmin","parameters":[{"in":"path","name":"email","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Superadmin retiré"}},"tags":["quotas"]}},"/v1/systeme/etat":{"get":{"operationId":"etat_systeme","responses":{"200":{"description":"Mode pare-feu, bans, état de bascule"}},"summary":"État plateforme (P7-08) : pare-feu (mode, bans) + bascule multi-site\n(site actif, santé des deux sites) pour la bannière de la console.","tags":["systeme"]}},"/v1/systeme/instances-sd":{"get":{"operationId":"instances_sd","responses":{"200":{"description":"Cibles http_sd des instances"}},"summary":"Découverte Prometheus (http_sd, P8-03) : cibles de métriques des\ninstances réelles — public : adresses seules, aucun secret.","tags":["systeme"]}},"/v1/usecases":{"get":{"operationId":"lister_usecases","parameters":[{"description":"slug d'org : ajoute l'état installé par use case","in":"query","name":"pid","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Catalogue des projets exemples"}},"tags":["donnees"]}},"/v1/usecases/{slug}":{"get":{"operationId":"detail_usecase","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Détail : explication, schéma, plan"}},"tags":["donnees"]}},"/v1/usecases/{slug}/plantuml":{"get":{"operationId":"plantuml_usecase","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Schéma PlantUML du use case (text/plain, formes natives par rôle)"}},"summary":"Schéma PlantUML du use case — bandes en packages, formes natives\npar genre (cylindre = données, queue = messagerie, person =\nutilisateur…), flèches étiquetées. Rend hors ligne partout.","tags":["donnees"]}},"/v1/usecases/{slug}/tofu":{"get":{"operationId":"tofu_usecase","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"main.tf OpenTofu équivalent au plan (text/plain)"}},"summary":"Code OpenTofu du use case — assemblé depuis les ressources RÉELLES\ndu provider ; le jeton ne figure jamais dans le fichier (CIRRO_TOKEN\nvient de l'environnement, cf. provider \"cirro\").","tags":["donnees"]}},"/v1/users":{"get":{"operationId":"lister_users","responses":{"200":{"description":"OK"}},"tags":["systeme"]},"post":{"operationId":"creer_user","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewUserReq"}}},"required":true},"responses":{"201":{"description":"Utilisateur créé"}},"tags":["iam"]}},"/v1/users/{id}":{"delete":{"operationId":"supprimer_user_api","parameters":[{"description":"Identifiant usr-… ou email du compte","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Compte supprimé — appartenances retirées, clés API révoquées, audit préservé"},"403":{"description":"Superadmin requis"},"404":{"description":"Utilisateur introuvable"}},"summary":"Supprime DÉFINITIVEMENT un compte utilisateur (superadmin uniquement) :\nappartenances d'organisation retirées, clés API révoquées, ligne user\ndétruite. L'AUDIT RESTE : la chaîne HMAC ne se tronque jamais (même\nrègle que `DELETE /v1/superadmin/orgs/{slug}`). Résout le compte par\nidentifiant (`usr-…`) ou par email.","tags":["systeme"]}},"/v1/vpn/etat":{"get":{"operationId":"etat_vpn","responses":{"200":{"description":"État du VPN : interface, peers, handshakes"}},"tags":["reseau"]}},"/v1/vpn/init":{"post":{"operationId":"init_vpn","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InitVpnReq"}}},"required":true},"responses":{"201":{"description":"Interface WireGuard initialisée (clés générées)"}},"tags":["reseau"]}},"/v1/whoami":{"get":{"operationId":"whoami","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhoamiRep"}}},"description":"Identité et adhésions"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErreurProblem"}}},"description":"AUTH_REQUISE / JETON_INVALIDE / CLE_INVALIDE — jeton ou clé API absent/invalide"}},"tags":["auth"]}},"/v1/zones":{"get":{"operationId":"catalogue_zones","responses":{"200":{"description":"Zones géographiques (nantes, reims, rennes, ovh) + zones de disponibilité — socle du placement et de la data source Terraform cirro_zones"}},"tags":["compute"]}},"/version":{"get":{"operationId":"version","responses":{"200":{"description":"OK"}},"tags":["systeme"]}}},"tags":[{"description":"Connexion, MFA, identité","name":"auth"},{"description":"Organisations, projets, membres","name":"iam"},{"description":"Clés API","name":"cles"},{"description":"Chaîne d'audit","name":"audit"}]}